A lightweight switcher of 2–3 mutually exclusive segments.
Overview
-
1
Two segments
-
2
Three segments
-
3
Icon + text
-
4
With Indicator
-
5
Skeleton
States
A segment is Standard (Figma: Default) or Active — exactly one is Active at all times, there is no "nothing selected" and no Disabled: neither the Figma set nor either platform ships one (the old web reference invented it; hide the control instead). The third Config is the Skeleton.
Standard · Active — live
-
1
Standard Transparent background; text and icon in --double-color-outline / --double-color-bg — one DoubleColor pair per state, so a two-tone icon and its label always flip together. Figma names this state Default.
-
2
Active The thumb (--background-inverse-primary, radius var(--sp-s16), no shadow) slides under the segment; text and icon flip to --double-color-outline-inverse / --double-color-bg-inverse — the Update 3.1 route (in Day the rendered tint is still the brand lime; the semantic path and the Night value changed).
Skeleton
-
1
The Whole Track Config=Skeleton — one var(--sp-s56) rectangle at radius var(--sp-s20) on --skeleton-on-white with the DS shimmer (--pattern-shimmer); no segments, no thumb.
Anatomy
A track, one sliding thumb, and 2–3 equal segments. Each segment carries text, an icon, or icon + text, plus an optional Indicator.
Parts
-
1
Track var(--sp-s56) tall, var(--sp-s4) padding, radius var(--sp-s20), --surface-on-white — re-pointable to any
backgroundgroup token via --segmented-control-fill (Update 3.1). -
2
Thumb The active plate: var(--sp-s48), radius var(--sp-s16), --background-inverse-primary, no shadow. One thumb — it slides, segments never repaint their own background.
-
3
Segment Equal widths, var(--sp-s48) tall, paddings var(--sp-s10) / var(--sp-s12), content centred; the label is Heading 4, one line, clipped without an ellipsis.
-
4
Icon + Label The icon is var(--sp-s24), two-tone on the DoubleColor pair; var(--sp-s4) to the label. Icon-only, text-only and icon + text all stage.
-
5
Indicator The DS Indicator (Simple or Number), var(--sp-s8) after the content, off by default; it inherits all of its own styles and motion.
Layout
The control fills the host's width; segments split it equally. The outer margins are s8 by default and belong to the host ("can be changed on any number from the DS").
Geometry · RTL
-
Box var(--sp-s56) — the track height.var(--sp-s4) — the track padding, all sides.var(--sp-s20) / var(--sp-s16) — the track / thumb-and-segment radii.var(--sp-s48) — the segment and thumb height.
-
Gaps var(--sp-s0) — between segments: they touch, only the track padding separates them from the edge.var(--sp-s4) — icon to label; var(--sp-s10) / var(--sp-s12) — the segment paddings.var(--sp-s8) — content to Indicator.
-
RTL A set axis: the row and the thumb mirror. The web measures the thumb from the segment's actual box, so
dir="rtl"is enough. Recorded: Android mirrors explicitly; iOS carries no RTL code (implicit mirroring over a raw offset) — flagged.
Animation
The Figma contract names one motion for switching: "Base pattern — Position medium".
-
1
The Thumb Slides --pattern-position-medium-appear — transform only (the width follows for rounding), 400 ms standard-ease-in-out. Recorded divergence: Android slides at 300 ms FastOutSlowIn and swaps colours instantly; iOS matches the contract.
-
2
The Colours Cross-Fade --pattern-color — the segment text and the icon's DoubleColor pair swap on the same switch, 200 ms. Colour transitions ride state changes only — the movement itself is transform.
-
3
The Indicator Appears On Its Own "The element inherits all motion from the origin element" — the DS Indicator's --component-scale-s-appear / --component-scale-s-hide, nothing re-declared here.
-
4
Reduced Motion Handled at the tokens — --pattern-position-medium-appear, --pattern-color and --pattern-shimmer all zero in
motion.css; the old component-local motion pair with its own override is retired.
Usage
-
1
A Lightweight Switcher 2–3 views, filters or modes of one surface — "a lightweight switcher between services". Selection is immediate; the host reacts to the change.
-
2
Not Navigation, Not Actions Screen-to-screen navigation is TabBar; an on/off is Switcher; a tap that performs something is Button — a segment only selects.
-
3
2–3 Segments, Never More The set stages Config 2 | 3 and iOS hard-caps the API at three. More options — Chips (choice) or a list. Labels stay one line and clip without an ellipsis: keep them short.
Accessibility
One control, one selection. Full spec: segmented-control-a11y.md. The Figma voiced previews are the contract: [Selected, To building, new items, Button, 1 of 2].
Semantics
-
1
A Radio Group
role="radiogroup"(with anaria-labelnaming what is switched) overrole="radio"buttons witharia-checked— the native web pattern that reproduces both contract facts, "Selected" and "N of M". One tab stop; arrow keys move and select (roving tabindex), Home/End jump to the edges. -
2
The Indicator Joins The Label
role="img"+ a phrase ("new items", "two new items") right after the label — the web equivalent of the platforms' appended description; never a separate focus stop. Icons arearia-hidden; the thumb too — the selection lives inaria-checked. -
Selection Is Never Colour Alone The thumb's plate, the DoubleColor flip and aria-checked carry it together. The focus ring is the family var(--sp-s2) inside outline in --text-and-icon-primary on
:focus-visible. -
Platforms Both ship Button + Selected (not tabs or radios): iOS voices "N of M" via a localized hint; Android wires
CollectionInfo/CollectionItemInfoand appends the Indicator's description to the label. No disabled state exists anywhere. -
Contrast Active on the thumb ≈ 12.6:1 Day (--double-color-outline-inverse on --background-inverse-primary); default ≈ 13.9:1 (--double-color-outline on --surface-on-white); the thumb itself ≥ 3:1 against the track.