Segmented control

Switches between a few views of one thing.

Sizes

  • AllErrorsWarnings

    sm

  • AllErrorsWarnings

    defaultdefault

  • AllErrorsWarnings

    lg

Multiple

  • AllErrorsWarnings

    falsedefault

  • true

States

  • Errors

    rest

  • Errors

    hover

  • Errors

    held

  • Errors

    focus-visible

  • Errors

    chosen

  • Errors

    disabled

Usage

When to use it
Use
Two to five views of the same content: all, errors, warnings; day, week, month.
Use
A filter where any number can be on at once (multiple).
Not for
A setting saved with a form, where each choice needs a sentence. Use Radio group.
Not for
A single on or off. Use Switch.
Not for
Pages with their own content and URL: use tabs.

Rules

7 rules
Must
Pass value or defaultValue to a single control. Because nothing is chosen until the first press, and a radio can never go back to none, so the view below has no state to show.
Must
Name the control with aria-label or aria-labelledby. Because a screen reader announces "radio group" with no question, then the bare segment names.
Must
Put a number in count, never in the label. Because count is tabular, reserves its width and is read as a description, so the name stays "Errors" while it ticks and the segments never move.
Never
Fill the track or a segment with a hue, or restyle the chosen thumb. Because the small glaze thumb is the chosen segment, the press recipe the ticked box and the switch share, and a hue reads as a status.
Should
Keep it to two to five segments with labels of one or two words. Because a sixth segment or a long label squeezes every segment, and a label that ends in an ellipsis hides the view's name.
Should
Pass countMax when a count changes live, so its slot is reserved from the start.
Should
Match size to the buttons in the same row: sm in a toolbar of sm buttons.

State order

6 states, highest first
1
disabled
2
chosen
3
held
4
focus-visible
5
hover
6
rest

Accessibility

5 checks
Single is a radiogroup with one tab stop; each segment is a radio. Multiple is a group of toggle buttons with aria-pressed.
The count is aria-hidden and reaches the segment as its description.
The chosen segment reads from the glaze thumb under it and its white label, 6.7:1 or more, against text-2 ones, not from colour.
A label cut by an ellipsis keeps its full text as the title and the name.
Focus is the 2px ink ring around the segment.

Keyboard

3 keys
Arrow keys
Single: move to the next segment and choose it. Multiple: move focus only.
Space
Chooses the focused segment, or turns it on or off with multiple.
Tab
Enters on the chosen segment and leaves the control.

Edge cases

7 edge cases
Count goes from 99 to 100
The slot was reserved for the widest value seen, or countMax, so nothing moves.
Narrow container
Labels end in an ellipsis; with fill the segments share the width equally.
Disabled segment
Arrow keys skip it; its label turns the disabled colour.
Right to left
Segments run right to left and the arrows follow.
Changing the choice
Single: one thumb slides to the new segment and takes its width in --dur-slide while the labels crossfade, and reduced motion jumps. Multiple: each pressed segment is its own thumb, no slide.
Dark theme
The thumb turns light glaze with a dark label on the dark paper track.
Forced colors
The track draws a CanvasText border; the chosen segment takes Highlight.

API

6 props
multiple
boolean default false
value, defaultValue, onValueChange
string | string[]
size
'sm' | 'default' | 'lg' default 'default'
fill
boolean default false
countMax
number
SegmentedControlItem value, count, countMax, disabled
string, number, number, boolean

Tokens

22 tokens
--paper
#ffffff
--line-faint
rgb(5 5 5 / 0.06)
--hover
rgb(5 5 5 / 0.06)
--text-1
rgb(5 5 5 / 0.80)
--text-2
rgb(5 5 5 / 0.60)
--text-disabled
rgb(5 5 5 / 0.32)
--glaze-sm-top
#6b6b6b
--glaze-sm-mid
#3d3d3d
--glaze-sm-bottom
#232323
--glaze-sm-highlight
rgb(255 255 255 / 0.30)
--glaze-sm-rim
rgb(0 0 0 / 0.55)
--glaze-sm-text
rgb(255 255 255 / 0.92)
--control-sm
26px
--control
32px
--control-lg
36px
--radius-pill
999px
--type-label
500 13px/1.35 var(--font-sans)
--type-label-sm
500 12px/1.35 var(--font-sans)
--type-label-lg
500 14px/1.35 var(--font-sans)
--focus
rgb(5 5 5 / 0.80)
--dur-slide
200ms
--ease-out
cubic-bezier(0.2, 0.8, 0.2, 1)

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.