Segmented control

Switches between a few views of one thing.

Examples

Conversation state with counts

AllWaiting on youEscalated

Show several at once

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
multiple
boolean default false
value / defaultValue
string | string[]
onValueChange
(value) => void
size
'xs' | 'sm' | 'default' default 'default'
fill
boolean default false
countMax
number
Item count
number
Item kind
'agent' | 'key' | 'run' | 'deploy' | 'model'
Item value, disabled, aria-label
string, 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

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.