MinecraftButton

A pixel stone menu button that cracks every few taps, shatters into chips, and drops itself as a bobbing item you pick up to respawn it.

MinecraftButton looks like a Java Edition menu button carved out of stone: a blocky black outline, a light and dark bevel, and a pixel label with a hard drop shadow. Hover it and your cursor becomes a diamond pickaxe; press to wind up, release to strike. Every hit chips off a few stone pixels. Every fifth tap the crack spreads further, and on the twentieth the button shatters and drops itself as a small spinning block. Click the drop to pick it up and put the button back.

Preview

Props

Install

Add the item with the shadcn CLI.

npx shadcn@latest add @evilbuttons/minecraft-button

The label reads a pixel font from the --font-pixel CSS variable and falls back to monospace. With next/font:

import { Pixelify_Sans } from "next/font/google";

const pixel = Pixelify_Sans({ variable: "--font-pixel", subsets: ["latin"] });

// <html className={pixel.variable}>

Usage

import { MinecraftButton } from "@/components/evil-buttons/minecraft-button";

export function ButtonDemo() {
  return (
    <MinecraftButton
      tapsPerStage={5}
      stages={4}
      onCrack={(stage) => console.log("Crack stage", stage)}
      onBreak={() => console.log("Broken.")}
    >
      Singleplayer
    </MinecraftButton>
  );
}

Props

The component spreads native button props. onClick fires on every tap before it counts; call event.preventDefault() to skip that tap.

PropTypeDefaultDescription
childrenReact.ReactNode-Button label. Falls back to label.
labelReact.ReactNode"Singleplayer"Label used when no children are provided.
tapsPerStagenumber5Taps needed to advance one crack stage.
stagesnumber4Stages until it breaks. The button breaks after tapsPerStage * stages taps.
soundbooleantruePlay synthesized dig, crack, break, and pickup sounds (Web Audio, no files).
pickaxebooleantrueSwap the mouse cursor for a pixel pickaxe that winds up on press and strikes on release.
respawnAfternumber-Respawn automatically after this many ms. Omit to wait for the drop to be picked up.
onCrack(stage: number) => void-Fired when the crack deepens to a new stage.
onBreak() => void-Fired when the button breaks.
onRespawn() => void-Fired when the button comes back.
classNamestring-Extra classes merged onto the button.

Notes

  • Everything snaps to a 3px texel grid: the seeded stone texture, the bevels, the crack pixels, and the flying chips.
  • Cracks are generated to fit the button's size and grow along branches, so each stage extends the previous one instead of swapping in a new pattern. A respawned button cracks along a new pattern.
  • The pickaxe is a portal-rendered sprite that follows the pointer, only for mouse and pen, never touch. The native cursor comes back over the dropped item and after the button breaks.
  • On break, focus moves to the dropped item; picking it up returns focus to the button.
  • Chips, shakes, pickaxe swings, and the item bob are skipped when the user prefers reduced motion. Cracks still show.

Registry

The registry item includes components/evil-buttons/minecraft-button.tsx and adds clsx and tailwind-merge as dependencies.

Search docs

Jump to a button documentation page