Segmented control
Switches between a few views of one thing.
Examples
AllWaiting on youEscalated
Sizes
- 1h24h7d
defaultdefault
- 1h24h7d
xs
- 1h24h7d
sm
Fill
- 1h24h7d
falsedefault
- 1h24h7d
true
States
- 1h24h7d
rest
- 1h24h7d
hover
- 1h24h7d
pressed
- 1h24h7d
focus-visible
- 1h24h7d
disabled item
- 1h24h7d
disabled
Usage
When to use it
- Use
- Two to five views of the same content: time range, conversation state, monthly or yearly.
- Use
- Filters that combine, such as errors and warnings shown together (multiple).
- Not for
- The views are separate pages or panels with their own content. Use Tabs.
- Not for
- The choice is a form value saved later. Use Radio group.
- Not for
- There are more than five options. Use Select.
- Not for
- One setting is on or off. Use Switch.
Rules
8 rules
- Must
- Name the control with aria-label or aria-labelledby. Because the control is a radiogroup, so without a name a screen reader announces "24h, radio" with no question.
- Must
- Pass defaultValue or value in single mode; it is empty until the first press and cannot be cleared. Because with nothing selected the view has no state to show, and a radio cannot be cleared back to none.
- Should
- Pass count on each segment when the views hold different amounts, and countMax for a live count.
- Should
- Write one or two words per segment, of about equal length.
- Must
- Use Select past five segments; the control cannot wrap or scroll. Because a sixth segment overflows the box on a narrow screen, since the control never wraps or shrinks a label below 6 characters.
- Must
- Set aria-label on a segment that holds only an icon. Because the glyph has no text, so a screen reader announces an unnamed radio.
- Never
- Fill a segment with a kind's hue; the selected segment is always a paper thumb on the grey track, and the kind goes in a Badge beside the control. Because the lifted thumb is how a person sees the choice, and a pastel segment reads as a label, not a selected one.
- Never
- Animate a pill sliding between segments; the selection cuts. Because the selected fill is the segment itself, so a thumb drawn apart lags arrow keys and drops the forced-colors frame.
Accessibility
7 checks
- The root is a radiogroup, or a group with multiple, named by aria-label.
- Each segment is a radio, or a button with aria-pressed in multiple mode.
- The control is one tab stop; Tab focuses the selected segment.
- Arrow keys move and select in single mode and move focus only in multiple mode; arrows skip disabled segments.
- Space and Enter select a segment, or toggle it in multiple mode.
- The count is aria-hidden and read as the segment description.
- Forced colors keep a Highlight fill on the selected segment and GrayText on disabled ones.
Edge cases
8 edge cases
- Long label
- The label box fades its last 16px, the title carries the full text, and segments shrink to a floor.
- Seven labels in 320px
- The track cannot wrap or scroll and runs past its container. Use Select past five.
- Count passes 99 to 100
- The slot is reserved for countMax, so no segment moves.
- Right to left
- Segments mirror and arrow keys follow the visual order.
- Zoom 200%
- The track is max(--control, 1lh + 12px) and grows with the text.
- No value in single mode
- Nothing is selected until the first press; it cannot return to none.
- On the inverse surface
- In the dark theme the thumb lifts 20% toward ink and gains a 16% ring.
- Slow or failed view
- The view shows its own loading or error under the control; the control stays put.
API
9 props
multiplebooleandefaultfalsevalue / defaultValuestring | string[]onValueChange(value) => voidsize'xs' | 'sm' | 'default'default'default'fillbooleandefaultfalsecountMaxnumberItem countnumberItem kind'agent' | 'key' | 'run' | 'deploy' | 'model'Item value, disabled, aria-labelstring, boolean, string
Tokens
20 tokens
--surface-card- oklch(var(--grey-c))
--surface-thumb- color-mix(in oklch, var(--paper), oklch(var(--ink-c)) var(--thumb-lift))
--elev-thumb- 0 0 0 1px oklch(var(--ink-c) / var(--thumb-ring-alpha)), 0 1px 2px oklch(var(--text-on-field-c) / calc(var(--shadow-alpha) * 0.5))
--thumb-lift- 0%
--thumb-ring-alpha- 0
--radius-inner- 4px
--radius-soft- 6px
--control- 32px
--control-sm- 28px
--control-xs- 24px
--space-3- 12px
--space-4- 16px
--text-2- oklch(var(--ink-c) / 0.80)
--text-1- var(--ink)
--surface-hover- oklch(var(--ink-c) / 0.05)
--text-disabled- oklch(var(--ink-c) / 0.40)
--surface-pressed- oklch(var(--ink-c) / 0.08)
--line-focus- 2px
--ease-step- cubic-bezier(0.2, 0, 0, 1)
--fade-line-end- 16px