SegmentedControl

SegmentedControl is used to pick one choice from a linear set of closely related choices, and immediately apply that selection.

Page navigation navigation

A SegmentedControl is treated like a list where each list item contains a button.

Do not treat a SegmentedControl like any of the following ARIA roles:

These roles have different keyboard rules and may confuse assistive tech users if used for SegmentedControls.

Keyboard navigation

Tab

Tab navigates through focusable elements. When entering the control:

  • Focus goes to the first button if coming from before the control.
  • Focus goes to the last button if coming from after the control.

Enter or Space

Selects a focused, unselected button. Pressing again on a selected button does nothing.

Arrow Left or Arrow Right

Arrow keys don't change focus or selection, unlike in radio groups or toolbars.

Accessibility and usability expectations

SegmentedControl provides people with the ability to select a choice from a small, focused group, and immediately activate the selected option.

Presentation

Cognition

  • Each SegmentedControl segment has a clear and succinct label (using a noun or short phrase) that informs users as to the purpose of the control
  • When icon-only segments are used, ensure that they have a clear label that conveys equivalent information
  • There must be a visual cue for the selected segment

Vision

  • When using icons, ensure that they have at least a color contrast ratio of 3:1 against the segment’s default and selected states
  • Ensure that text has a color contrast ratio of at least 4.5:1

Mobility

  • Segments must have a minimum target size of 24×24 CSS pixels. This is to ensure that the button is large enough to be easily activated by users with moving disabilities. The entire touch area should respond to taps, clicks, and hovers even if it isn't directly on the control.
Edit in Figma

Adaptability

  • Make sure that text can be increased up to 200%, and any visible labels remain visible
  • Segments must remain readable and not require horizontal scrolling to read or operate them, at a viewport size of 320 by 256 CSS pixels
  • When adding additional text spacing, make sure that visible labels can still be read in their entirety

Interaction

Keyboard

  • Each segment must receive focus when navigating with the Tab key
  • Enter or Space must select the segment
  • Segments must receive focus in a logical order, which in the majority of cases, will be the order that they are visually displayed

Touch and mouse

  • Segments must be activated on the up event, and not the down event, or there must be a way to prevent the action from taking place on the down event

Engineering for compatibility with assistive technology

Screen reader

  • The order in which the segments are implemented should result in a logical reading order
  • A group of segments must have an accessible name, to provide people with context as to what they’re choosing and why
  • Segments that feature an icon and visible label should only use the visible label text in their accessible name
  • Segments labelled using only an icon must have an accessible name, which you can add with the aria-label prop
  • Segments must programmatically convey when they are selected
  • If used to complete an action on the same page, without changing the URL, the segmented control button must have a role of button, to help convey how people can interact with it
  • If used to navigate to a different page, changing the URL, the segmented control button must have a role of link, to help convey how people can interact with it
    • If using a link, ensure that the control is labelled consistently across different pages

Speech recognition

  • Segments that have a visible label, or associated tooltip, must have an accessible name that includes that content (ideally at the start of the accessible name)

Built-in accessibility features

This component is rendered as an unordered list element (<ul>), with each segment rendered inside a list item element (<li>). This helps to programmatically convey the grouping of the buttons to people who cannot see them.

<ul>
  <li>
    <button aria-current="true">… Preview …</button>
  </li>
  <li>
    <button aria-current="false">… Raw …</button>
  </li>
  <li>
    <button aria-current="false">… Blame …</button>
  </li>
</ul>

When selected, each segment (rendered as a button) conveys its state using aria-current=“true”. Screen readers will announce this information, to help people understand which of the segments are active.

Implementation requirements

Group label

The SegmentedControl component must be given a group label to help convey the purpose of the grouping, and provide context as to what it relates to.

This can be achieved in a few different ways:

  • If there’s already a visible label that relates to the SegmentedControl component, use the aria-labelledby prop to reference the id of the visible label, providing the component with an accessible name that matches the label's content. For example:
<label id="sort-by-label" …>Sort by</label>
<SegmentedControl aria-labelledby="sort-by-label" …>
  <SegmentedControl.Button defaultSelected>New</SegmentedControl.Button>
  <SegmentedControl.Button>Top</SegmentedControl.Button>
</SegmentedControl>
  • If there isn’t a visible label, use the aria-label prop to provide the group with a name, as demonstrated in the following example:
<SegmentedControl aria-label="File view">
  <SegmentedControl.Button defaultSelected>Preview</SegmentedControl.Button>
  <SegmentedControl.Button>Raw</SegmentedControl.Button>
  <SegmentedControl.Button>Blame</SegmentedControl.Button>
</SegmentedControl>

Button labels

Similar to the aria-label provided for the group, when using icons in a segment, an aria-label must be used to provide the control with an accessible name.

If the button contains an icon and visible label, the aria-label prop must repeat the visible label text.

<SegmentedControl aria-label="File view">
  <SegmentedControl.Button defaultSelected aria-label="Preview" leadingIcon={EyeIcon}>
    Preview
  </SegmentedControl.Button>
  …

If the button contains only an icon, the aria-label prop must provide equivalent information, as that conveyed by the use of the icon.

<SegmentedControl aria-label="File view">
  <SegmentedControl.IconButton defaultSelected aria-label="Preview" icon={EyeIcon} />
  …

How to test the component

Integration tests

  • If the SegmentedControl's segments are visible at wide viewports, ensure they are not hidden at narrow viewports
  • If the SegmentedControl dynamically adds content to the page, focus management is implemented so that keyboard users can efficiently navigate to the new content and screen reader users don't miss any new content
  • If the SegmentedControl is used for regular page navigation/reloading, no focus management is implemented

Component tests

  • The SegmentedControl component is exposed as an unordered list element (<ul>) with an appropriate accessible name
  • Each SegmentedControl segment is exposed as either a button or link, with an appropriate accessible name, wrapped in a list item element (<li>)
  • Where a segment has a leading icon, it uses an aria-label which matches the visible label text
  • The selected segment has an aria-current=“true” attribute
  • The component’s buttons remain readable and do not require horizontal scrolling when resized or zoomed to a viewport of 320px
  • Each SegmentedControl button meets the minimum target size requirement of 24 by 24 CSS pixels
  • SegmentedControl components do not have more than 2-5 choices, or 6 icon-only segments