HoldConfirmButton

HoldConfirmButton guards actions behind a deliberate press-and-hold. While held, a thick gooey mint ring draws clockwise around the pill and the button squashes down with a spring. Release early and progress rewinds; hold to completion and onConfirm fires.

Preview

HoldConfirmButton

Press and hold

Fast hold

Fast hold

Hold for 1.2s

Install

Add the item with the shadcn CLI.

npx shadcn@latest add @evilbuttons/hold-confirm-button

Usage

[]txt
import { HoldConfirmButton } from "@/components/evil-buttons/hold-confirm-button";

export function ButtonDemo() {
  return (
    <HoldConfirmButton
      label="Hold to confirm"
      duration={2000}
      onConfirm={() => console.log("confirmed")}
      onAbort={(progress) => console.log("aborted at", progress)}
    />
  );
}

Props

PropTypeDefaultDescription
durationnumber2000Milliseconds the user must hold before onConfirm fires.
labelReact.ReactNode"Hold to confirm"Label shown while idle.
holdingLabelReact.ReactNodelabelLabel shown while holding.
successLabelReact.ReactNode"Confirmed"Label shown after a successful hold.
minScalenumber0.9Smallest scale reached at 100% progress.
ringSizenumber280Diameter of the progress ring in pixels.
ringStrokeWidthnumber12Stroke width of the progress ring.
ringColorstring"#5eead4"Color of the progress ring.
onConfirm() => void-Fired when the hold reaches 100%.
onAbort(progress: number) => void-Fired when released early with progress reached (0-1).
resetAfternumber1600Ms to stay in success before resetting. 0 stays.

Notes

  • Uses Motion press, motionValue, mapValue, styleEffect, and svgEffect so one progress value drives both the scale and ring.
  • The ring SVG is rotated -90deg so progress starts at the top.
  • data-state is idle | holding | success for styling hooks.

Registry

The registry item includes components/evil-buttons/hold-confirm-button.tsx and installs motion, clsx, and tailwind-merge as dependencies.