Hold to confirm

A delete you can't hit by accident. Press and hold, and colour fills the key from the start while it trembles, barely at first and harder toward the end. Let go early and the fill drains back. Hold to the end and it confirms with a pop.

launch-plan-final.pdf2.4 MB

Installation

Terminalbash
npx @neelshha/ui@latest add hold-to-confirm

Usage

page.tsxtsx
import { HoldToConfirm } from "@/components/ui/hold-to-confirm";

<HoldToConfirm onConfirm={remove} doneLabel="Deleted">
  Hold to delete
</HoldToConfirm>

It's for actions that are hard to undo but common enough that a dialog would nag. The fill runs at a steady rate, so it shows how much longer to hold. Say "hold" in the label: nothing else tells people a click won't do.

Tones

Red for deleting, the blue key for shipping, ink for signing. A longer duration makes it a more deliberate act.

page.tsxtsx
<HoldToConfirm tone="key" doneLabel="Deployed" onConfirm={deploy}>
  Hold to deploy
</HoldToConfirm>
<HoldToConfirm
  tone="ink"
  duration={2500}
  doneLabel="Signed"
  onConfirm={sign}
>
  Hold to sign
</HoldToConfirm>

From a shortcut

held lets a shortcut of your own hold it. This one holds while D is down.

or hold D

page.tsxtsx
// True while D is down, from your own key listener.
<HoldToConfirm held={holdingD} onConfirm={remove}>
  Delete
</HoldToConfirm>

Keyboard

Hold Space or Enter while it has focus. Screen readers and switch controls click without pressing first, and they can't hold, so that kind of click confirms at once. The label has to say what the button does.

API

Other props go to the button. A long press on a touch screen won't open a menu partway through the hold. With reduced motion it doesn't tremble or pop, but the hold still takes the whole duration. It's a safeguard, not motion.

PropTypeDefaultDescription
childrenReactNodeThe label, like "Hold to delete".
onConfirm() => voidRuns once a hold lasts the whole duration.
durationnumber1500How long to hold, in milliseconds.
doneLabelReactNodeShown for a moment once it confirms.
heldbooleanfalseHolds it from outside, for a shortcut of your own. After a confirm it has to go false before it can hold again.
tone"danger" | "key" | "ink""danger"The colour it fills with.