Getting started
Foundations
Integrations
Form19
Display5
Layout4
Navigation4
Overlay8
Feedback4
Motion19
#Installation
npx @dinachi/cli@latest add hold-to-confirm#Usage
tsx
import { HoldToConfirm } from "@/components/ui/hold-to-confirm"tsx
<HoldToConfirm onConfirm={deleteProject} confirmedLabel="Deleted">
Hold to delete
</HoldToConfirm>#Examples
#Variants
| Variant | Behaviour | Use it when |
|---|---|---|
fill | A bar sweeps the button from the left, behind the label. | Most cases. The most legible of the three, and the only one that reads at a glance across a room. |
ring | A stroke closes around the button. | Square icon-only buttons, where there is no width for a bar to travel. |
border | The button's own outline draws itself. | A hold inside dense UI. Nothing moves behind the label, so it is the quietest option. |
#When to use it
A hold buys the same deliberation as a confirmation dialog without taking over the screen, and it leaves the action where the user found it.
| Use it for | Not for |
|---|---|
| Deleting a project, revoking a key, ending a session. | Anything repeated. A hold costs a second and a half every time, and a control hit ten times a day is resented by the third. |
| Actions with no undo. | Anything reversible. An undo is faster to offer and cheaper to ignore. |
#Behaviour
- Asymmetric timing. Progress takes
durationto complete but unwinds in 200ms. Committing should feel considered, backing out should feel free. - Resumes, never restarts. Release unwinds from the current position, so letting go and pressing again does not start over.
- Survives a small drag. Pointer capture keeps events arriving once the finger leaves the button.
#Accessibility
- Space and Enter hold, and key up releases. Auto-repeat is ignored, so a held key is one hold rather than a restart on every repeat.
- Blur cancels. A hold that continues once the button is unfocused is one the user can no longer watch.
- Give a
ringbutton anaria-label. It has an outline and an icon, and neither is a name. data-holdinganddata-confirmedexpose both states for styling, so press feedback does not have to ride on:active.- Reduced motion drops the press scale and keeps the progress. The progress is the affordance, not decoration: a hold that counts invisibly has no feedback at all.
#API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "fill" | "ring" | "border" | "fill" | How progress is drawn |
| duration | number | 1600 | Milliseconds the user must hold to confirm |
| onConfirm | () => void | — | Fired once the hold completes |
| resetAfter | number | 1600 | Milliseconds the confirmed state is held before resetting. Set 0 to stay confirmed |
| confirmedLabel | ReactNode | — | Replaces the label while confirmed |
| fillClassName | string | — | Class applied to the progress fill |
| render | RenderProp | — | Render as a different element |