Segmented control

Switches between a few views of one thing.

Sizes

  • AllErrorsWarnings

    sm

  • AllErrorsWarnings

    defaultdefault

  • AllErrorsWarnings

    lg

Multiple

  • AllErrorsWarnings

    falsedefault

  • true

Count

  • AllErrors

    countdefault

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; hour, day, week.
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
Actions side by side, nothing chosen. Use Button group.

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
Give the control or a segment a fixed width; let the labels size it, or pass fill. Because a fixed width clips a longer label or a translation, and the track is meant to be as wide as its words.
Never
Show the chosen segment with the slab recipe or a status colour. Because the slab is the one action on the screen and a chosen view is not an action; the checked recipe draws it in yellow as a state, with an ink label and no halo, which is how Sodium shows a value chosen.
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
Match size to the buttons in the same row: sm in a panel head 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 is yellow with a gold edge and an ink label (12.8:1) in light and the yellow tint with a yellow edge and label (9.0:1) in dark; the others are text-2 on the off-white body, 7.3:1 (9.9:1 in dark).
A label cut by an ellipsis keeps its full text as the title and the name.
Focus is the 2px --focus ring around the segment, raised over its neighbours.

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

6 edge cases
Count goes from 99 to 100
The slot was reserved for the widest value seen, or countMax, so nothing moves.
Longer label
The segment grows with it; nothing clips. With fill the segments share the width and a label too long ends in an ellipsis.
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
The fills crossfade on --dur-light and the new segment's light rises; nothing slides.
Forced colors
Segments draw ButtonText edges; the chosen one takes Highlight, unlit.

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

24 tokens
--check-off
var(--secondary-bg)
--check-off-hover
#ffffff
--well
#f4f3ee
--line
#e2dfd5
--text-1
#161512
--text-2
#55524a
--text-disabled
#a8a499
--check-bg
#ffd400
--check-edge
#d9b400
--check-mark
#161512
--primary-light
rgb(255 255 255 / 0.55)
--primary-light-hover
rgb(255 255 255 / 0.85)
--dur-light
240ms
--dur-glow
320ms
--ease-soft
cubic-bezier(0.22, 0.8, 0.24, 1)
--control-sm
26px
--control
32px
--control-lg
36px
--z-raised
10
--type-label
400 13px/16px var(--font-mono)
--type-label-sm
400 12px/16px var(--font-mono)
--type-label-lg
400 14px/20px var(--font-mono)
--focus
#7a5d00
--dur-state
120ms

Install

For your coding agent

GitHub

Please star me pookie

198

A new design system every week

Leave your email before you copy the prompt.