Components

Segmented Control

A horizontal group of mutually exclusive options. One segment is always active. All segments share equal width.

v3.1.0 Status: Draft Updated 2026-05-19
States
Active segment uses --background-inverse-primary thumb with --shadow-s. Default segments are transparent. Disabled segments use muted text and ignore pointer events.
Active (first) / Default / Default — 3 segments
With double-color start-slot icons
Disabled segment (middle)
StateElementToken
ActiveThumb background--background-inverse-primary
ActiveThumb shadow--shadow-s
ActiveLabel color--text-and-icon-brand
ActiveIcon vector layer--double-color-outline-inverse
ActiveIcon bg layer--double-color-bg-inverse
DefaultSegment backgroundtransparent
DefaultLabel color--text-and-icon-primary
DefaultIcon vector layer--double-color-outline
DefaultIcon bg layer--double-color-bg
DisabledLabel color--text-and-icon-disabled
TrackBackground--surface-on-white
Anatomy
Track contains 2–5 equal-width segments. Each segment is composed of 4 optional/required slots arranged in a horizontal row.
Track with annotated segment slots
ATrack — --surface-on-white
BThumb — --background-inverse-primary
Cstart-slot — IconContainer (dc, optional)
Dlabel — Heading 4, required
Eindicator — Indicator L, optional
Fend-slot — IconContainer (dc, optional)
SlotTypeRequiredNotes
TrackContainerRequiredHolds all segments + sliding thumb. role="group" + aria-label
ThumbAbsolute overlayAutoInjected by JS. Slides under active segment via transform: translateX()
start-slotIconContainer (dc)OptionalLeft icon. 24×24px. Does not flip in RTL
labelTextRequiredHeading 4. Single line — truncates with ellipsis. Gap to icon: --sp-s4
indicatorIndicator · LOptionalStatus dot after label. Gap: --sp-s8. Collapses when hidden
end-slotIconContainer (dc)OptionalRight icon. 24×24px. Does not flip in RTL
Layout
All spacing uses SP tokens. Track fills 100% of its container. Segments always share width equally — no horizontal scrolling.
ElementPropertyTokenValue
Trackheight--sp-s5656px
Trackpadding (all sides)--sp-s44px
Trackborder-radius--sp-s2020px
Thumbheight--sp-s4848px
Thumbborder-radius--sp-s1616px
Segmentheight--sp-s4848px
Segmentpadding horizontal--sp-s1212px
Segmentpadding vertical--sp-s1010px
Segmentborder-radius--sp-s1616px
Segmentgap (icon → label)--sp-s44px
Segmentgap (label → indicator)--sp-s88px
2 segments — minimum
3 segments — recommended default
4 segments
5 segments — maximum; short labels required
With start-slot icons
RTL (dir="rtl") — segment order reverses, icons do not flip
Animation
The active thumb slides via transform: translateX() — compositor-friendly. Color transitions use the Patterns/Color curve (linear, 200ms).
Live demo — click segments to watch thumb animate
ElementPropertyDS patternDurationCurve
Thumbtransform: translateXTransforming/State/Slow300mscubic-bezier(0.4, 0, 0.2, 1)
ThumbwidthTransforming/State/Slow300mscubic-bezier(0.4, 0, 0.2, 1)
LabelscolorPatterns/Color200mscubic-bezier(0.25, 0.25, 0.75, 0.75)
Icons (.dc-bg, .dc-vector)fillPatterns/Color200mscubic-bezier(0.25, 0.25, 0.75, 0.75)
Curve rationale — Thumb uses standard-ease-in-out because it moves within the screen (Rule: Transforming/State). Colors use linear because there is no geometry change — only a color swap (Patterns/Color).
Reduced motion — @media (prefers-reduced-motion: reduce) sets --sc-thumb-motion and --sc-color-motion to 0ms linear. The thumb jumps instantly; labels and icons switch without transition.
Usage
Segmented Control is best for switching between a small set of persistent, mutually exclusive views or modes.
When to use
  • 2–5 options that are mutually exclusive
  • Persistent mode switching (view mode, sort order, content type)
  • The user needs to compare options before choosing
  • All options should always be visible — none hidden behind overflow
When not to use
  • More than 5 options — use a Select or bottom sheet instead
  • Independent toggles (on/off per item) — use individual Toggle Switches
  • Navigation between pages — use Tabs or bottom navigation
  • Labels are long or variable — they will truncate with ellipsis
ComponentUse instead when…
TabsSwitching between full page views or sections with scroll position
Toggle SwitchEach option is independent (on/off), not mutually exclusive
Select (Dropdown)More than 5 options, or options need a description/icon beyond a short label
Radio Button GroupForm context where options are listed vertically and need a visible label above
Accessibility
All segments are focusable <button> elements. The track has role="group" with a descriptive aria-label. Active state is communicated via aria-pressed.
ElementRoleAttributeValue
Track (.segmented-control)grouparia-labelDescriptive name, e.g. "View mode"
Active segmentbutton (implicit)aria-pressed"true"
Inactive segmentbutton (implicit)aria-pressed"false"
Disabled segmentbutton (implicit)aria-disabled + disabled"true"
KeyBehavior
TabMoves focus into the control (first or last focused segment)
Shift+TabMoves focus out of the control
Tab withinMoves focus to the next segment
← ArrowMoves focus to the previous segment (wraps around) and activates it
→ ArrowMoves focus to the next segment (wraps around) and activates it
Space / EnterActivates the focused segment; updates aria-pressed
All segments are in the tab order (tabindex="0"). Do not suppress outline on .sc-segment — the browser default focus ring is sufficient. Minimum tap target: --sp-s48 height (enforced by segment height). Full accessibility spec: specs/components/segmented-control/segmented-control-a11y.md