Pixel Button
A retro pixel-style button with chunky layered shadows, animated dot texture, and a satisfying press interaction for a classic arcade feel.
Installation
npx shadcn@latest add https://fluxbuttons.vercel.app/r/pixel-button.json"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
export interface PixelButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children?: React.ReactNode
buttonColor?: string
shadowColor?: string
outlineColor?: string
borderColor?: string
dotColor?: string
shadowLayers?: number
}
const PixelButton = React.forwardRef<HTMLButtonElement, PixelButtonProps>(
({
className,
children,
buttonColor = "#facc15",
shadowColor = "#292524",
outlineColor = "#fafaf9",
borderColor = "rgba(255, 255, 255, 0.3)",
dotColor = "rgb(255 255 255 / 80%)",
shadowLayers = 6,
...props
}, ref) => {
const [isActive, setIsActive] = React.useState(false)
const generateBoxShadow = (layers: number) => {
const shadows: string[] = []
for (let i = 1; i <= layers; i++) {
const offset = i * 0.5
shadows.push(`${offset}px ${offset}px 0 0 ${shadowColor}`)
}
shadows.push(`0 0 0 2px ${outlineColor}`)
for (let i = 1; i <= layers + 2; i++) {
const offset = i * 0.5
shadows.push(`${offset}px ${offset}px 0 2px ${outlineColor}`)
}
return shadows.join(', ')
}
const defaultShadow = generateBoxShadow(shadowLayers)
const hoverShadow = `0 0 0 2px ${outlineColor}`
return (
<>
<style dangerouslySetInnerHTML={{
__html: `
@keyframes dots {
0% {
background-position: 0 0, 4px 4px;
}
100% {
background-position: 8px 0, 12px 4px;
}
}
.dots-animate::before {
animation: dots 0.5s infinite linear;
}
`
}} />
<button
ref={ref}
className={cn(
"text-base cursor-pointer relative font-bold leading-none p-[1px]",
"rounded-full text-stone-800 text-center transition-all duration-150",
"outline-2 outline-transparent outline-offset-[5px]",
"hover:translate-x-0 hover:translate-y-0",
"focus-visible:outline-dashed",
className
)}
style={{
transform: 'translate(-4px, -4px)',
boxShadow: defaultShadow,
backgroundColor: shadowColor,
outlineColor: buttonColor,
}}
onMouseEnter={(e) => {
e.currentTarget.style.transform = 'translate(0, 0)'
e.currentTarget.style.boxShadow = hoverShadow
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translate(-4px, -4px)'
e.currentTarget.style.boxShadow = defaultShadow
}}
onMouseDown={() => setIsActive(true)}
onMouseUp={() => setIsActive(false)}
{...props}
>
<div
className="relative pointer-events-none border-2 rounded-full dots-animate"
style={{
backgroundColor: buttonColor,
borderColor: borderColor
}}
>
<div
className="absolute inset-0 rounded-full opacity-50"
style={{
backgroundImage: `
radial-gradient(${dotColor} 20%, transparent 20%),
radial-gradient(rgb(255 255 255 / 100%) 20%, transparent 20%)
`,
backgroundPosition: '0 0, 4px 4px',
backgroundSize: '8px 8px',
mixBlendMode: 'hard-light'
}}
/>
<span
className={cn(
"relative flex items-center justify-center px-5 py-3 gap-1 transition-transform whitespace-nowrap",
isActive && "translate-y-[2px]"
)}
style={{
filter: 'drop-shadow(0 -1px 0 rgba(255, 255, 255, 0.25))'
}}
>
{children || "Button Hover Me"}
</span>
</div>
</button>
</>
)
}
)
PixelButton.displayName = "PixelButton"
export default PixelButtonUsage
import BrokenButton from "@/components/ui/broken-button";
export default function Usage() {
return <BrokenButton />;
}Examples
Button Color
import PixelButton from "@/components/ui/pixel-button";
export default function Usage() {
return <PixelButton buttonColor="#15fab5ff">Click Me</PixelButton>;
}Shadow Color
import PixelButton from "@/components/ui/pixel-button";
export default function Usage() {
return <PixelButton shadowColor="#15fab5ff">Click Me</PixelButton>;
}Outline Color
import PixelButton from "@/components/ui/pixel-button";
export default function Usage() {
return <PixelButton outlineColor="#15fab5ff">Click Me</PixelButton>;
}Shadow Layer
import PixelButton from "@/components/ui/pixel-button";
export default function Usage() {
return <PixelButton shadowLayers={10}>Click Me</PixelButton>;
}API Reference
Pixel Button
The Pixel Button component is a wrapper around the button element that adds a variety of styles and functionality.
Prop
Type