Designer view

Segmented Control

A row (or column) of connected buttons that lets the user switch between mutually-exclusive views of the same content — list vs grid, day vs week vs month, KB vs MB vs GB unit toggles. Distinct from Tabs (which switches between different *content*), distinct from RadioGroup (which captures form values rather than view state), distinct from Toggle (which is binary, not n-way). Used for content-presentation toggles where the underlying data is the same and the segments select the visual shape.

When to use

Use

For switching between mutually-exclusive views of the same content — list vs grid view, day vs week vs month calendar, KB vs MB vs GB unit toggle, light vs dark vs auto theme. The user picks one; the underlying data stays the same and the segment selects how it is presented.

Avoid

For switching between different content panels — that is `Tabs`. For form values with form submission — that is `RadioGroup` (which is semantically identical but visually distinct as separate radio buttons). For binary on/off — that is `Switch` or `Toggle`. For more than 5 options — that is a `Select` dropdown.

Versus related

  • tabs

    `Tabs` switches between different *content* panels; `SegmentedControl` switches between *view-of-same- content*. The semantic distinction drives the role (`tablist` vs `radiogroup`) and the keyboard model (Tabs may use manual activation; SegmentedControl always uses auto-activation per APG radiogroup).

  • radio

    `RadioGroup` is the semantic foundation — SegmentedControl IS a radio group, just rendered as connected-segment buttons rather than as separate radio inputs. Use Radio when the choices are part of a form being submitted (gender, plan tier); use SegmentedControl when the choice controls in-page view-presentation.

  • toggle

    `Toggle` (a.k.a. Switch) is binary on/off; `SegmentedControl` is n-way mutual exclusion. Two- segment SegmentedControls and Toggles can look similar — the distinguisher is whether the option space is binary (Toggle) or could expand (Segmented).

Highlight
Fig 1.1 · Segmented Control · Designer view
Designer

Figma anatomy

Slot Figma type Hint
root frame Auto-layout horizontal frame; segments share an outer border
segment instance Segment component instance with selected and unselected variants
icon instance Icon component instance per segment; visibility per variant
label text Segment text style; bound to a component property
indicator rectangle Pill or filled rectangle behind the selected segment; position bound to selection
Designer

Token usage per slot

root
radius
  • cornerradius.md
color
  • backgroundcolor.surface.sunken
  • bordercolor.border.subtle
segment
spacing
  • paddingspacing.compact
radius
  • cornerradius.sm
color
  • foregroundcolor.text.muted
  • ringcolor.border.focus
typography
  • sizetext.sm
  • weightweight.medium
icon
color
  • foregroundcolor.text.muted
label
color
  • foregroundcolor.text.muted
typography
  • sizetext.sm
  • weightweight.medium
indicator
radius
  • cornerradius.sm
color
  • backgroundcolor.surface.bg
elevation
  • shadowelevation.sm
Both

Figma ↔ Code property map

FigmaTypeCodeNotes
VariantVariantvariantMaps standard / icon-only.
SizeVariantsizesm / md / lg.
OrientationVariantorientationhorizontal / vertical. Affects keyboard model (ArrowLeft/Right vs ArrowUp/Down).
Has IndicatorBooleanhasIndicatorToggles the sliding-indicator slot. Default true; some variants use only foreground-color contrast for selection without a separate indicator.
Segment CountVariantsegments.lengthFigma exposes 2/3/4/5 segment counts as a Variant for preview-time layout review. Code accepts an array of segment definitions.
Selected SegmentVariantvalueFigma exposes "first / second / third selected" as Variant for preview; in code this is a controlled value (the selected segment id).
Has IconsBooleanhasIconsToggles per-segment icons. Required for icon-only variant; optional for standard variant.
Designer

Motion

TransitionDuration token
indicatormotion.duration.fast
segmentColorChangemotion.duration.fast
Easing
motion.easing.standard
Reduced motion
Instant (jump cut)
Designer

Responsive behaviour

BreakpointChange
breakpoint.smAt and below, the canonical default switches to compact density and may force `orientation="vertical"` when the segment count exceeds 3 (avoids overflow on narrow viewports). For icon-only variant, orientation stays horizontal up to 4 segments because icon-only is space-efficient.
breakpoint.mdAbove this width, orientation and size render as authored. Both orientations are valid; choose per layout context.
Both

Internationalisation

RTL · mirroring

Horizontal orientation reverses logically — first segment in source order appears at the inline-start (visual right in RTL). ArrowLeft / ArrowRight follow visual direction (per APG radiogroup canonical): ArrowRight moves to the *previous* segment in RTL, ArrowLeft to the *next*. Vertical orientation is direction-neutral (Up/Down keys unchanged). Indicator animation slides along the logical axis.

Text expansion

Segment labels grow with translation. The control sizes to its widest segment (or to fill the container when `width: full`); long-text languages may force wider segments than designed. Avoid fixed segment widths; allow intrinsic-size growth. Above 4 segments with long-text labels, consider switching to vertical orientation or to Tabs.

Both

Variants, properties, states

Variants

Structurally different versions of the component.

standardicon-only

Properties

The same component, parameterised.

PropertyType
sizesm | md | lg
orientationhorizontal | vertical
hasIndicatorboolean

States

Browser/user-driven (interactive) vs. app-driven (data).

KindStates
interactive
hoverfocus-visibleactivedisabled
data
selected
Both

State transitions

FromToTrigger
selectedselectedUser activates a different segment via Enter / Space or via ArrowKey navigation in roving-tabindex mode (which auto-activates on focus per APG radiogroup contract). The previously-selected segment loses `aria-checked="true"`; the newly-selected segment gains it. The indicator (if present) animates to the new position.
Both

Figma↔Code mismatches

  1. 01
    Figma

    SegmentedControl drawn as Tabs with no visual differentiation

    Code

    A SegmentedControl with `role="radiogroup"` rather than `role="tablist"`

    Consequence

    Designers may use SegmentedControl visually for what is semantically Tabs (switching between different content panels). Implementations following the design ship `role="radiogroup"` for content-switching, breaking the keyboard model (radiogroup uses ArrowKeys with auto-activation; tablist may use manual activation) and misleading SR users about the surface's purpose.

    Correct

    Distinguish at the canonical level: SegmentedControl switches view-of-same-content (list vs grid; day vs week vs month); Tabs switches between different content panels. The semantic distinction drives the role (`radiogroup` vs `tablist`). Document the boundary clearly in `whenToUse.vsRelated`.

  2. 02
    Figma

    Multi-select segmented control drawn (multiple segments highlighted)

    Code

    A canonical SegmentedControl is single-select only; multi-select segments are not segmented control

    Consequence

    Designers compose mocks with two or three segments "active" simultaneously. Implementations following the design ship multi-select; the semantic surface is neither radiogroup (single) nor a clean checkbox group (no visible grouping). The control becomes ambiguous.

    Correct

    SegmentedControl is single-select by canon — `role="radio"` semantics enforce mutual exclusion. For multi-select visual toggles, use a horizontal `<CheckboxGroup>` styled to look segmented; document this as the related pattern, not as a SegmentedControl variant.

  3. 03
    Figma

    Selected segment differentiated by a coloured fill alone

    Code

    Selected segment differentiated by fill PLUS contrast-text PLUS optional border

    Consequence

    Designers may use a single colour change to mark selection. Implementations following the design fail WCAG 1.4.1 (Use of Color) for users with colour-vision deficiencies; SR users hear the same announcement for every segment because the differentiation is purely visual.

    Correct

    Triple-layer selection differentiation: visual (background + foreground contrast + optional weight or border), accessible state (`aria-checked="true"`), and iconographic (an inline checkmark or selected-state icon for icon-only variants). The `aria-checked` is the source of truth; visuals reinforce.

  4. 04
    Figma

    Vertical segmented control drawn but no orientation property documented

    Code

    A SegmentedControl with an `orientation` property switching between row and column layouts

    Consequence

    Designers compose vertical-segmented variants in mocks without flagging that the orientation is property-driven. Developers ship one orientation; the design is occasionally requested in the other and re-implemented from scratch.

    Correct

    Document `orientation` as a first-class property (horizontal default, vertical opt-in). The keyboard model adapts: ArrowLeft / Right for horizontal, ArrowUp / Down for vertical, with Home / End working in both.

Designer

Common mistakes

#segmented-as-tabs

Used as content-switching tabs

Problem

The control is used to switch between different content panels (a settings page with "Account / Billing / Notifications" sections). Semantically this is Tabs; the `role="radiogroup"` of SegmentedControl is wrong here — SR users hear "radio button" instead of "tab", and panel content does not get the `role="tabpanel"` relationship.

Fix

Use Tabs for content-switching. SegmentedControl is for view-of-same-content (list vs grid). The decision test: does activating a segment change *what content is shown* or *how the same content is displayed*? "What" → Tabs; "how" → SegmentedControl.

#segmented-no-roving-tabindex

Every segment has tabindex="0"

Problem

Tab cycles through every segment one by one. APG canonical for radiogroup is roving tabindex — only the currently-checked segment has `tabindex="0"`, others have `tabindex="-1"`. ArrowKeys navigate within the group.

Fix

Roving tabindex — only the checked segment is in the tab order. ArrowKeys move focus AND selection (per APG radiogroup auto-activation). Tab from the radiogroup moves to the next document focusable, not to the next segment.

#segmented-arrow-no-activate

ArrowKeys move focus but do not activate

Problem

Implementation copies the manual-activation pattern from Tabs. ArrowKeys move focus between segments; the user must press Enter / Space to activate. APG radiogroup canonical is auto-activation — ArrowKey IS the activation.

Fix

ArrowKey moves focus AND activates the new segment. `aria-checked` flips immediately. The indicator (if present) animates to the new position. Enter / Space are no-ops on the focused segment when it is already checked (or are equivalent to ArrowKey activation if a rare focus-without-check edge case exists).

#segmented-color-only-selected

Selection differentiated by colour alone

Problem

Selected segment has a filled background; unselected segments have transparent background. The differentiator is colour. Colour-vision deficient users cannot distinguish; SR users get only the `aria-checked` cue.

Fix

Pair colour with non-colour cue: contrast-text foreground change (selected segment uses primary text colour vs muted; unselected uses muted), optional weight change, optional inline checkmark for icon-only-variant. Colour is reinforcement, not the sole signal.

#segmented-overflow-silent

Overflowing segments clipped at the inline-end

Problem

Five-segment control rendered in a narrow container; the last two segments clip at the edge. Users cannot see them; SR users navigate to them via ArrowKey but pointer users miss their existence.

Fix

For SegmentedControl above 5 segments, redesign as tabs with overflow-scroll, a Select, or splitting into smaller controls. Document the canonical maximum segment count in performance. Above the threshold, SegmentedControl is the wrong pattern.

Accessibility hints
Slot Accessibility hint
root Apply `role="radiogroup"` plus an `aria-label` (or `aria-labelledby` referencing a heading near the control). The radiogroup is the canonical APG choice because segments are *mutually-exclusive choices about view*, not navigation between content. Tabs would be wrong — Tabs switch content, segments switch view-of- content.
segment Each segment is `role="radio"` with `aria-checked` reflecting selection state. Roving tabindex — only the currently-checked segment has `tabindex="0"`, others have `tabindex="-1"`. Disabled segments use `aria-disabled` and stay focusable so SR users hear them.
icon Decorative when paired with a visible label (`aria-hidden="true"`). For icon-only variant, the segment's `aria-label` carries the meaning ("List view", "Grid view") because the icon alone is not a label.
label Plain text. When icon-only variant is used, the label is hidden visually but kept in the DOM (visually- hidden) for SR users — never replaced by `aria-label` without a visible alternative when the segment also renders text.
indicator Decorative — `aria-hidden="true"`. Selection is communicated through `aria-checked` on the radio segments; the indicator is visual reinforcement.