Components › Forms & input › Choice › Segmented control

Segmented control

canon

Inline group of mutually exclusive buttons for switching modes.

acrm !4066 (28 Sep 2026): SegmentedOption.count?: number — neutral .acrm-count after the label (CountBadge look, never accent), 0 not drawn; first use segment detail "Vše | Přírůstky (N)" (replaced .sgb-ctl chips). 30 Sep 2026 (acrm !4252, AD FA218): option prop tone?: positive | warning | critical colours only the TEXT of the selected option (.on.seg-tone-*) with --positive-ink / --warning-ink / --critical-ink (AA ≥ 5.3:1 light and dark). Only for an answer with meaning (yes / maybe / no, e.g. meeting RSVP), never decoration; selection still carried by the surface and aria-pressed.

Used in 58 files · 35 areas · 71× — components/accounting, components/agent-desk, components/assistant, components/callwrapup, components/clen-tymu, components/costs …

Keyboard

  • Each segment is a native <button type="button">: Tab moves through every segment, Enter or Space selects.
  • There is no roving tabindex or arrow-key model — the control is a group of toggle buttons, not a radiogroup.
  • No disabled prop; a segment cannot be disabled through <Segmented>.

ARIA

  • Container: role="group" with aria-label from the ariaLabel prop — always pass it.
  • Selected segment: aria-pressed="true"; others aria-pressed="false". The .on class is the visual twin.
  • The CSS also honours [aria-selected='true'] for legacy markup, but <Segmented> renders aria-pressed.
  • Icons render through <Icon> (decorative, aria-hidden handled by the icon component); the label text is the accessible name.

Focus

  • Foundation rule in ds.css: :where(button, …):focus-visible { outline: 2px solid var(--accent); outline-offset: 1px } — applies to every segment.
  • The ring is drawn outside the 26px segment, over the 2px track gutter; no component-specific focus style.

Contrast

Computed from packages/ui/src/tokens/tokens.css (WCAG 2.x relative luminance):

  • Selected label --fg #18191b on --surface-100 #ffffff = 17.59:1 (light); #eceded on #1b1c1f = 14.53:1 (dark).
  • Unselected label --fg-secondary #7d8184 on track --line-100 #f4f4f5 = 3.57:1 (light) — below AA 4.5:1 for the 12px 500 label; #9a9ca1 on #232529 = 5.59:1 (dark).
  • Selected surface white on track #f4f4f5 = 1.1:1 — the selected state relies on --shadow-xs and label weight, not on a 3:1 boundary.
  • Focus ring --accent on canvas: 4.44:1 (light), 3.83:1 (dark).
  • Toned selected label (tone, acrm !4252) on --surface-100: positive #297a3a 5.33:1 / #62c073 7.55:1, warning #a35200 5.58:1 / #ff990a 7.96:1, critical #cb2a2f 5.37:1 / #ff6166 5.80:1 (light / dark) — all AA. The tone never replaces the selected surface or aria-pressed.

Known limits

  • Unselected label contrast in light theme (3.57:1) is below AA for normal text.
  • Selected-state boundary (white on --line-100) is below 3:1; the state is also carried by aria-pressed and the darker label.
  • No arrow-key navigation inside the group; with many segments Tab presses add up (keep to ≤5).
  • No disabled or loading state.

Source: docs/choice/segmented-control.a11y.md · GitLab