A swipe-to-confirm action control: a track with a draggable Slider pulled from one edge to the other to commit, resolving into a Loading → Success or Error result. For high-consequence actions where an accidental tap would be costly.
Overview
The SlidingButton has one size preset and a custom base:
-
1
Main
-
2
Custom
States
Eight states across the full lifecycle. On tap and On drag are the interaction phases before the result cycle; Loading resolves to exactly one of Success or Error, and Error returns the control to Standard.
Thresholds
-
1
Standard
-
2
On tap
-
3
On drag
-
4
Loading
-
5
Success
-
6
Error
-
7
Disabled
-
8
Skeleton The empty track shape alone, filled by Skeleton — no Slider, label or icon.
Styles
The colour token used by every element, per state.
default state
- 1Slider Background ColorBackground/Brand
- 2Slider IconTextAndIcon/AlwaysDark
- 3TitleTextAndIcon/InversePrimary
- 4SubtitleTextAndIcon/InversePrimary
- 5Track BackgroundBackground/InversePrimary
on drag state
- When the Slider reaches 50% of the Track's width, the Title and Subtitle start changing color to the TextAndIcon/InversePrimary.
- 1Slider Background ColorBackground/Brand
- 2TitleTextAndIcon/AlwaysDark
- 3SubtitleTextAndIcon/AlwaysDark
- 4Slider IconTextAndIcon/AlwaysDark
- 5Track BackgroundBackground/Brand
loading state
- 1LoadingBackground/Brand — background colorAlwaysDark — Loader style
success state
- 1Track Background in on drag and success statesBackground/Brand
- 2Success IconTextAndIcon/AlwaysDark
error state
- 1Error State BackgroundAccent/Red1
- 2Error IconTextAndIcon/AlwaysLight
disabled state
- 1Slider Background ColorSurface/OnGrey
- 2Slider IconTextAndIcon/Disabled
- 3TitleTextAndIcon/Disabled
- 4SubtitleTextAndIcon/Disabled
- 5Track BackgroundSurface/OnWhite
Skeleton
- generalHow skeleton works
Styles (Custom)
By default the Custom version repeats exactly the styles listed for the states above — every value there is a default that can be reconfigured or switched off.
Anatomy
-
1
Slider Sliding element — built on the CustomSquircle base (
.squircle .sliding-button__slider). -
2
StartText Contains Title and Subtitle (
.sliding-button__text). -
3
Slider Track The track the Slider moves along (
.sliding-button__background). -
4
Loader Shown in the Loading state (
.sliding-button__loader). -
5
Check icon Shown in the Success state (
.sliding-button__check). -
6
Close icon Shown in the Error state (
.sliding-button__close).
Layout
The six measurement groups mirror the Figma Layout frame: Track, Slider, StartText, and the three result states. Every value is an SP token; corner and StartText padding scale to a max of 130%.
-
1
Track width — auto-layout fill: Flexible(iOS); FillMax(Android).var(--sp-s68) — horizontal padding.var(--sp-s64) min height — auto-layout fill.var(--sp-s20) (130%) — corner size.
-
2
Slider var(--sp-s4) — horizontal padding.var(--sp-s4) — vertical padding.var(--sp-s16) (130%) — corner size.var(--sp-s56) min height.var(--sp-s56) min width.var(--sp-s24) — icon size —
forward-arrow-right.var(--sp-s32) — left padding — in on tap state. -
3
StartText var(--sp-s0) — horizontal padding.var(--sp-s4) — vertical padding.var(--sp-s2) — spacing between Title and Subtitle.Heading 4 — Titles text style.Compact Body — Subtitles text style.Title and Subtitle — line limit 1, truncated — by default.Title and Subtitle — center alignment.
-
4
Loading L — Loader component size (var(--sp-s32) host).
-
5
Success var(--sp-s32) — icon size —
check. -
6
Error var(--sp-s32) — icon size —
close.
Layout (Custom)
Same six groups as above — every value is the by-default, overridable per instance in the Auto Layout size and spacing settings.
RTL
RTL is opt-in through the component's RTL axis (Off by default). When on, the whole control mirrors — shown here on Standard. The label is the only part that stays put.
- SliderRests at the end (right) edge and is dragged right → left. The same 50% / 80% thresholds and the whole result cycle apply, mirrored.
- IconThe directional
forward-arrow-rightflips to point left (scaleX(-1)) — a directional glyph per the icon-container RTL rules. - Title / SubtitleStay centred — the text is not mirrored, only the Slider and its icon.
Animation
Drag either Slider — past 50% the label recolours; releasing at ≥80% enters Loading, then the labelled outcome. Release below 80% and it springs back. Each demo resolves to a fixed result so Success and Error can be seen on their own. The transitions between states follow the Figma Animation model — every one maps to a DS motion token.
-
Default → On Tap → Default Leading padding → var(--sp-s32) — Enter (finger down) on var(--transforming-state-fast), Exit (finger up) on var(--pattern-scale-short-hide).iOS haptic — Tap, at the moment of touch. Android plays no haptic.
-
On tap → Default The Slider tracks the pointer 1:1 on a Linear curve (var(--curve-linear)) — no timed token, the speed is the finger’s. Past 50% the label recolours on var(--pattern-color).
-
On Drag → Default Releasing below 80% returns the Slider to Default on var(--pattern-scale-medium-appear) (Scale Medium).
-
Loading → Success The Loader gives way to the result on var(--component-fade-m-appear) (fadeM — opacity + scale); the spinner rotates on var(--pattern-spin).iOS haptic — Success. Android plays no haptic.
-
Loading → Error The Loader gives way to the
closeglyph on var(--component-fade-m-appear) (fadeM) and the fill turns var(--accent-red1).iOS haptic — Error. Android plays no haptic. -
Error → Default The control returns for another attempt on var(--component-fade-m-hide) (fadeM) — the result fades out and the fill returns to var(--background-brand) / var(--background-inverse-primary).
-
Reduced motion Every token drops to 0ms and the spinner to none. Zeroed at the token in motion.css, so the component carries no override of its own.
Usage
A swipe costs more effort than a tap by design. Spend it where an accidental commit would be costly; on a low-stakes action it just trains people to swipe without reading.
-
1
Use for Confirming a payment, starting or ending a ride, sending an SOS, deleting an account — a single deliberate commitment, ideally with the inline Loading → Success / Error result.
-
2
Not for An ordinary action that commits on contact — that is Button. Navigation without committing — that is a link. An on/off setting — that is Switch.
Accessibility
Screen reader
Sliding buttons need a degree of interaction precision that is a poor experience with a screen reader on. So expose it and let it be activated the way a normal button is — a double-tap (or Enter), not a swipe — and drop the word “Slide” from the accessible name: with VoiceOver or TalkBack on, a swipe navigates and does something different, so the name is the action alone (e.g. “Start ride”).
-
1
Name and role role="button" — the control commits one action and has no adjustable value, so role="slider" would misdescribe it.The accessible name is the action alone — “Slide” is omitted (it would mislead when a swipe is a navigation gesture).
-
2
The Slider is not exposed The drag is a pointer affordance with no assistive-technology equivalent. Screen-reader and keyboard users activate the button (double-tap / Enter / Space); arrow keys do nothing.The 50% / 80% drag thresholds are pointer mechanics — not announced.
-
VoiceOver · iOS Voice preview: “{action}, Button.”Label {action} · Value n/a · Traits Button · Hint n/a.
-
TalkBack · Android Voice preview: “{action}, Button, Double-tap to activate.”ContentDescription {action} · State n/a · Role Button · Action Double-tap to activate.
-
Loading aria-busy — the name is kept and the control is not activatable. VoiceOver announces it as busy; TalkBack sets a “Loading” state, announced through a live region.
-
Success The outcome is announced once through a live region — VoiceOver posts an announcement, TalkBack an accessibilityLiveRegion. The control is terminal and no longer offers the action.
-
Error The failure is announced the same way — through the polite live region — then the control returns to Standard, announced and activatable again, not signalled by the red fill alone.
-
Disabled VoiceOver appends “Dimmed” and keeps it in the rotor; TalkBack appends “Disabled”, drops it from swipe navigation and ignores the double-tap.
-
Full spec The per-platform label / value / trait / hint tables, edge states and RTL live in the a11y spec. specs/components/sliding-button/sliding-button-a11y.md