Slide to Confirm
Confirm a costly action with a deliberate gesture: drag the thumb to the end of the track.
import { SlideToConfirm } from '@youj/react'
Installation & usage
npm i @youj/reactFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Use a Slide to Confirm for an action that is costly to trigger by mistake, such as a payment, a transfer or a deletion, where a dialog would interrupt more than it helps. For a lighter guard on a button, use hold on an Async Button.
'use client'
import { SlideToConfirm } from '@youj/react'
export function PayInvoice({ invoice }: { invoice: Invoice }) {
return (
<SlideToConfirm
label={`Slide to pay ${invoice.total}`}
confirmingLabel="Paying…"
confirmedLabel="Paid"
onConfirm={() => pay(invoice.id)}
/>
)
}onConfirm is awaited: resolve for success, reject or throw for an error. It runs once per confirmation, and a second confirm while it runs is ignored. Once confirmed the control stays confirmed; give it a new key to offer it again. It sizes to its container up to max-w-sm; pass a className to change that.
Description
A checkout's last step: slide to pay, then the thumb spins while the payment runs and lands on a check.
Drag the thumb past 90% of the track and let go to confirm; short of that it springs back. The track label fades as the thumb passes over it, and a wash fills in behind the thumb. Once confirmed, onConfirm runs: the thumb shows a spinner and confirmingLabel, then the whole track takes the success fill, the thumb shows a check and the label becomes confirmedLabel, as an Async Button confirms. It stays confirmed.
Deleting a workspace, with tone="danger".
Use the danger tone for an action that deletes or can't be undone: the thumb takes the danger fill and the track a danger wash.
A transfer that fails, in the small size.
When onConfirm rejects (or throws), the thumb turns to a cross and springs back to the start, and the error's message shows in the track and is announced. After resetAfter the label returns, and the person can slide again. Pass errorLabel to name the failure yourself.
Code
slide-to-confirm-demo.tsx, the Confirm payment story. The component itself comes from the package.
Reference
PropsonConfirm, label, confirmingLabel, confirmedLabel, errorLabel, size, tone…
Props
| Prop | Type | |
|---|---|---|
onConfirm | () => unknownThe action, run once the thumb reaches the end. Return a promise to show its progress: the thumb spins until it resolves, then shows a check and stays confirmed. A rejection (or a throw) shows its message in the track, springs the thumb back and lets the person try again. | – |
label | stringThe track's label and the thumb's name. default "Slide to confirm" | "Slide to confirm" |
confirmingLabel | stringShown and announced while default "Working…" | "Working…" |
confirmedLabel | stringShown and announced once confirmed. default "Confirmed" | "Confirmed" |
errorLabel | string | ((error: unknown) => string)Shown in the track and announced assertively when | – |
resetAfter | numberHow long an error shows before the track returns to rest, in ms. Default: 3 × the deliberate token. | – |
disabled | booleanBlocks the thumb and takes it out of the tab order. default false | false |
size | 'sm' | 'default' | 'lg'
default 'default' | 'default' |
tone | 'default' | 'danger'
default 'default' | 'default' |
sound | booleanWith sound on ( default true | true |
Other props pass through: extends ComponentProps<'div'>. | ||
Data attributes
The root carries data-state (idle, confirming, confirmed or error), data-size (sm, default or lg) and data-tone (default or danger), plus data-dragging while the thumb is held and data-disabled when disabled. The thumb's position is --ui--slide, from 0 to 1.
MotionDrag, spring back, outcome · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
| Dragthe thumb follows the pointer 1:1, never eased in time | no token | ||
| Spring backreleased short of 90%, the thumb returns to the start | spring.smooth0.36s, bounce 0 | 0.36s, bounce 0 | |
| Snap to the endreleased past 90%, the thumb lands at the end | spring.snappy0.2s, bounce 0 | 0.2s, bounce 0 | |
| Labelfades as the thumb passes; swaps to each state | duration.fastease.standard140ms · cubic-bezier(0.2, 0, 0, 1) | 140mscubic-bezier(0.2, 0, 0, 1) | |
| Thumb markchevron, spinner, check or cross swap with a scale from 0.75 and a fade | duration.fastease.standard140ms · cubic-bezier(0.2, 0, 0, 1) | 140mscubic-bezier(0.2, 0, 0, 1) | |
| Check landingthe check scales in on the spring | spring.smooth0.36s, bounce 0 | 0.36s, bounce 0 | |
| Success fillthe track washes to the success fill, as an Async Button’s tint | duration.fastease.standard140ms · cubic-bezier(0.2, 0, 0, 1) | 140mscubic-bezier(0.2, 0, 0, 1) | |
Error holdbefore the track returns to rest, unless resetAfter is set | duration.deliberate560ms · × 3 | 560ms× 3 |
Reduced motion
The thumb still follows the pointer, but it is back at the start, or at the end, at once. The thumb's marks swap at once, and the spinner holds a still quarter arc.
Reduced motion
Dragging works the same. The spring back and the snap are instant, the marks swap at once, and the spinner holds still.
AccessibilitySlider role, keyboard, announcements
Accessibility
- The thumb is a
slider(0 to 100) named bylabeland described by a hint: use the arrow keys or End to reach the end, then press Enter. - Arrow keys step by 10, Home and End jump to either end, and Enter at the end (90% or more) confirms. In right-to-left, the arrow keys follow the mirrored track. A thumb left short of the end goes back when it loses focus.
- While confirming and once confirmed, the slider is read-only and its value text is the state's label. Confirming and confirmed are announced politely, an error assertively.
disabledtakes the thumb out of the tab order and setsaria-disabled.