v3.0.0
SlidingButton

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.

-- -- -- 3.0.8

Overview

The SlidingButton has one size preset and a custom base:

  • 1
    Main
  • 2
    Custom
1
2

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

The Thresholds parameter sets the percentage of the Track width at which the Slider automatically moves to the next state. This value can be configured.The default threshold is 80%:• If the Slider moves less than 80% of the Track width, it returns to the Default state.• If the Slider reaches 80% of the Track width or more, it automatically moves to the Loader state.If the Loader state is disabled, the Slider moves directly to the Success or Error state.
1
2
3
4
5
6
7
8
  • 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

1
2
3
4
5
  • 1
    Slider Background ColorBackground/Brand
  • 2
    Slider IconTextAndIcon/AlwaysDark
  • 3
    TitleTextAndIcon/InversePrimary
  • 4
    SubtitleTextAndIcon/InversePrimary
  • 5
    Track BackgroundBackground/InversePrimary

on drag state

1
2
3
4
5
  • When the Slider reaches 50% of the Track's width, the Title and Subtitle start changing color to the TextAndIcon/InversePrimary.
  • 1
    Slider Background ColorBackground/Brand
  • 2
    TitleTextAndIcon/AlwaysDark
  • 3
    SubtitleTextAndIcon/AlwaysDark
  • 4
    Slider IconTextAndIcon/AlwaysDark
  • 5
    Track BackgroundBackground/Brand

loading state

1
  • 1
    LoadingBackground/Brand — background colorAlwaysDark — Loader style

success state

1
2
  • 1
    Track Background in on drag and success statesBackground/Brand
  • 2
    Success IconTextAndIcon/AlwaysDark

error state

1
2
  • 1
    Error State BackgroundAccent/Red1
  • 2
    Error IconTextAndIcon/AlwaysLight

disabled state

1
2
3
4
5
  • 1
    Slider Background ColorSurface/OnGrey
  • 2
    Slider IconTextAndIcon/Disabled
  • 3
    TitleTextAndIcon/Disabled
  • 4
    SubtitleTextAndIcon/Disabled
  • 5
    Track BackgroundSurface/OnWhite

Skeleton

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.

Designer — you need to know it! In the Custom version, you can modify or disable all style parameters of the component.

Anatomy

1
2
3
4
5
6
  • 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
var(--sp-s64) min height
var(--sp-s20) (130%) — corner size
var(--sp-s68) — horizontal padding
2
State = default · var(--sp-s16) (130%) corner
State = on tap · left padding var(--sp-s32)
3
State = default, Subtitle = on (short text)
State = default, Subtitle = on (long text)
State = default, Subtitle = off (long text)
State = default, Subtitle = on (long text without truncate)
4
State = loading
5
State = success
6
State = error
  • 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.

Designer — you need to know it! Every value above is the by-default. In the size and spacing settings of the Auto Layout panel you can change them to any S-size; the designer alone is responsible for any custom padding configuration (composition).

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.

Standard · LTR
Standard · RTL
  • 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-right flips 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.

Drag to the end → Success
Drag to the end → Error
  • 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 close glyph 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.
1
✓ A high-consequence confirm — an accidental tap would be costly
2
✕ An ordinary, low-stakes action — that is Button

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
2
  • 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