DoubtButton never lets you click just once. Each press swaps the label for the next, increasingly absurd confirmation prompt and gives a small shake, while a hairline along the bottom edge tracks how much doubt you have left to power through. Only the click on the final prompt fires onConfirm.
Preview
Props
Variants
One quick doubt
Pass a single confirmation for a lightweight "are you sure?" gate.
Maximum dread
A longer chain for the truly irreversible.
Install
Add the item with the shadcn CLI.
npx shadcn@latest add @evilbuttons/doubt-buttonUsage
import { DoubtButton } from "@/components/evil-buttons/doubt-button";
export function ButtonDemo() {
return (
<DoubtButton
label="Delete everything"
successLabel="Too late."
onConfirm={() => wipeDatabase()}
/>
);
}Props
The component spreads any <button> HTML attributes except onClick.
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | - | Label shown in the idle state. Falls back to label. |
label | React.ReactNode | "Delete everything" | Idle label used when no children are provided. |
confirmations | React.ReactNode[] | 6 escalating prompts | Prompts shown one per click; the last one fires the action. |
successLabel | React.ReactNode | "Too late." | Label flashed after the final confirmation. |
onConfirm | () => void | - | Fired only after the last confirmation is clicked. |
resetAfter | number | 1600 | Milliseconds to stay in the success state before resetting. Set to 0 to stay. |
className | string | - | Extra classes passed to the button. |
Notes
- The user must click
confirmations.length + 1times beforeonConfirmruns; an emptyconfirmationsarray makes it behave like a normal one-shot button. - The button shakes on every click via Motion, and the prompt slides in from below.
- An invisible copy of the widest prompt reserves the button's width so the layout never jumps as labels swap.
- A hairline progress bar along the bottom edge fills in as you answer each doubt.
- Styling uses the
primarytokens, so it follows your theme; override withclassName. aria-live="polite"anddata-state(idle|doubting|success) expose the current state for screen readers and styling.
Registry
The registry item includes components/evil-buttons/doubt-button.tsx and installs clsx, tailwind-merge, and motion as dependencies.