Segmented Control

How the Segmented Control works with a keyboard and assistive tech, and where it stands against WCAG 2.2 AA.

FreeReact and Vue
AAWCAG 2.2 target
5/6contrast pairs passing
8keyboard interactions

The Segmented Control is a radio group: exactly one segment is chosen, the group is one tab stop, and the arrow keys move the choice. It follows the WAI-ARIA Radio Group pattern, and meets WCAG 2.2 AA in its text and focus.

Keyboard#

Tab

Moves focus into the group, onto the chosen segment. A second Tab leaves the group.

Shift+Tab

Moves focus back out of the group.

→

Chooses the next segment and moves focus to it, wrapping from last to first and skipping disabled segments.

←

Chooses the previous segment, wrapping from first to last.

↓

Does the same as Right.

↑

Does the same as Left.

Home

Chooses the first enabled segment.

End

Chooses the last enabled segment.

Semantics#

role="radiogroup"

Groups the segments and is named by the field header through aria-labelledby, or by ariaLabel when there is no header.

Always
role="radio"

A <button> with the radio role, so it is announced as one choice of several, with its position.

Each segment
aria-checked

true on the chosen segment, false on the rest.

Each segment
tabindex="0" / "-1"

Only the chosen segment is in the tab order; the arrows move between the rest.

Roving
aria-label

Names an icon-only segment, so the glyph is not the only cue. The glyph itself is aria-hidden.

Icon only
disabled

Skipped by the arrow keys. aria-disabled on the group when the whole control is off.

Disabled segment

Colour contrast#

Every variant and state has to meet WCAG 2.2 Level AA. Measure the colours as they land on screen: a translucent hover or press wash over whatever sits behind it.

4.5:1

Segment label and glyph · Default, Hover, Chosen

The 12px Bold label against the track's 4% wash, or the chosen white face. An icon-only glyph shares the label colour and needs 3:1, which it clears.

WCAG 1.4.3 Contrast (Minimum) ↗WCAG 1.4.11 Non-text Contrast ↗
3:1 if needed

Chosen segment · Chosen

The chosen face is white on a #f5f5f5 track with a #f2f2f2 ring and a soft drop, close to 1.1:1. The label also darkens from #6d6d6d to #2a2a2a, a 2.77:1 step. The lift and the darker label together carry the state; don't rely on the face alone.

WCAG 1.4.11 Non-text Contrast ↗WCAG 1.4.1 Use of Color ↗
3:1

Focus indicator · Focus visible

A 2px #1a6ef8 ring with a white gap, 4.54:1 against white. On the chosen segment it sits outside the face's own ring.

WCAG 1.4.11 Non-text Contrast ↗WCAG 2.4.13 Focus Appearance ↗
Not required

Disabled · Disabled

Disabled segments are exempt. Their text is #cbcbcb; a chosen one keeps its white face and loses its lift.

WCAG 1.4.3 Contrast (Minimum) ↗

Measured contrast#

Segment text against what it sits on: the control's 4% track over white, and for Items the same 4% wash under a 24% white hover. 1 of 6 pairs fall below their minimum (4.5:1 for text, 3:1 for graphics) and are marked in amber.

Part · elementDefaultHoverChosen
Segmented Control · segment4.75passes13.17passes14.35passes
Items · item2.97below the minimum13.40passes14.35passes

Motion#

  • The chosen face and the label colour change over 150ms.
  • Items turn that off under prefers-reduced-motion. The track's segments are not in the reduced-motion rule yet; the change is a colour fade, not movement, but it should match.

Checklist#

Name the group: a visible field header, or ariaLabel when there isn't one.

Every icon-only segment has a label, read out in place of the glyph.

Items draw their resting text in #8f8f8f on the grey track, 2.97:1, under 4.5:1. Use #6d6d6d before the code library ships.

Choosing a segment changes the view at once. Don't use it where a change needs confirming or takes a moment to load.

Segments are 32px tall, above the 24px minimum. Keep to two or three; long labels truncate in a 300px track.