TrollButton refuses to be clicked, at first. As the cursor closes in it springs out of the way, leaning into each dodge, and when it's backed into a corner it slips past you to the other side. Every chase drains a thin stamina bar along its bottom edge. After a few seconds it runs out of breath, slumps back into place, and finally lets you click it.
Preview
Props
Install
Add the item with the shadcn CLI.
npx shadcn@latest add @evilbuttons/troll-buttonUsage
import TrollButton from "@/components/evil-buttons/troll-button";
export function ButtonDemo() {
return (
<TrollButton giveUpAfter={3000} onClick={() => console.log("Caught it")}>
Click If You Can
</TrollButton>
);
}Variants
Stubborn
A long chase over a wide area before it gives in.
Props
The component spreads native button props and forwards its ref to the button.
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | - | Button label. Falls back to label. |
label | React.ReactNode | "Click Me" | Label used when no children are provided. |
surrenderLabel | React.ReactNode | "Fine, click me" | Label shown once the button gives up. |
giveUpAfter | number | 3500 | Milliseconds of chasing before it gives up. The timer starts on the first dodge. |
fleeRadius | number | 56 | How close in px the cursor can get to the button's edge before it dodges. |
range | number | 140 | Maximum distance in px it strays from its resting spot. |
resetAfter | number | 1500 | Milliseconds after a successful click before it starts fleeing again. 0 keeps it caught. |
onClick | (event) => void | - | Only fires once the button has given up. |
className | string | - | Extra classes passed to the button. |
Notes
- Distance is measured from the button's resting slot plus its current offset, so dodges don't jitter as it moves.
- On touch there is no hover, so tapping makes it dodge instead, and those taps count toward the timer.
- Keyboard activation always goes through, and
prefers-reduced-motionturns the fleeing off entirely. - Shares the graded dark surface, hairline ring, and spring press used by MinimalButton and CommandButton. The stamina bar drains along its bottom edge while it's chased.
Registry
The registry item includes components/evil-buttons/troll-button.tsx and installs motion, clsx, and tailwind-merge as dependencies. The component imports cn from @/lib/utils, matching the project alias in components.json.