Segmented control
A segmented control lets users switch between a small set of mutually-exclusive views of the same content — List vs Map, a filter across one dataset, an in-place state toggle — without navigating away.
Quarantine
A segmented control lets a user pick one option from a small set that swap between mutually-exclusive views of the same content — List vs Map, an in-place filter, a state toggle. Selection happens in place: the user stays on the page and never loses their scroll position. Reach for it when there are two to three equal, always-visible options and the choice re-renders content rather than navigating away.
This component is fully responsive and intentionally designed across mobile, tablet, and desktop — no alternative versions required.
Anatomy
Section titled “Anatomy”- Segmented control
- Selector
- Label
When to use it
Section titled “When to use it”Use a segmented control when:
- Exactly one option can be active at a time.
- The options are different views or filters of the same content.
- Switching should happen in place, without a page load or navigation.
- There are two to three options — occasionally up to five.
Avoid a segmented control when:
- More than one option can be selected — use Checkbox or Chips.
- The choice is a binary on/off — use Toggle.
- The options are independent sections or pages — use Tabs.
- There are more than five options, or the labels are long — use a Dropdown / Select.
Choosing the right control
Section titled “Choosing the right control”Segmented control sits between Tabs and Toggle. Walk the decision guide from the top:
More than one can be selected?
The choices aren’t mutually exclusive — a user can pick several at once. Use Chips (compact, inline filtering) or Checkbox (forms, settings). Not a segmented control.
Binary on / off?
A single setting that’s either on or off, taking effect immediately. Use a Toggle. Not a segmented control.
Independent sections?
Each option reveals a distinct section or destination the user navigates between. Use Tabs. Not a segmented control.
Variations of the same content?
One dataset or view, switched between a few equal alternatives, in place. This is the segmented control.
Properties
Section titled “Properties”Segmented control
The enclosing container that holds the segments and reads as a single control. Groups the options with a shared, always-visible background so the set is legible as one unit, and rounds to a pill.
Selector
One option in the set. Segments are equal width — two to five distribute evenly and never need per-option width tuning. Each carries its own state — Rested, Hover, Selected, Disabled, Focus — each driven by its own design-token set.
Selected fill
A contained fill pill marks the one active segment; there’s no sliding indicator. Exactly one segment is selected at all times — the browser enforces this because the segments are same-name radios.
Size
Three text sizes — Small, Medium (default), Large — set the segment typography and padding to match the density of the surrounding context.
Icon
A segment can carry a leading icon before its label, or be icon-only — a compact square variant where labels are unnecessary. Icon-only segments still need an accessible name for screen readers.
Scheme
Neutral (default) reads on light surfaces; Contrast reads on dark or coloured surfaces. Brand (Toyota / Lexus) is inherited automatically — the control is never themed per brand.
Platform considerations
Section titled “Platform considerations”Desktop
Default Medium size. Keep the control near the content it switches so the cause-and-effect is obvious. Prefer two to three segments; the whole control should read at a glance without scanning.
Tablet
Same rules as desktop. Touch input is common — the whole segment is the tap target, so keep labels short enough that each segment stays comfortably tappable.
Mobile
Keep to two or three segments; long labels or five options crowd a narrow width — switch to a Dropdown instead. The Small size suits dense mobile toolbars.
Best practices
Section titled “Best practices”A segmented control should read as one compact switch. Keep the option set small, the labels parallel, and the active choice unmistakable.
Do
Keep two to three segments (five is the hard ceiling). Use short, parallel labels in sentence case. Make sure exactly one segment starts selected. Switch content in place — the user shouldn’t lose context. Keep the control next to what it controls.
Don't
Don’t use it for more than five options, or for long labels — reach for a Dropdown. Don’t mix it up with Tabs (independent sections) or Toggle (binary). Don’t allow zero or multiple selections. Don’t hide the selected state or rely on colour alone to signal it.
Content guidelines
Section titled “Content guidelines”Label each segment with one or two words, in sentence case, with no trailing punctuation (“Map”, “List”, “This week”). Keep the labels parallel — same part of speech, same grammatical shape — so the set reads as one choice. Never let a label wrap past two lines; if it needs more, the control is the wrong choice. For icon-only segments, still give every option a clear accessible name.
Styles
Section titled “Styles”A segmented control is a single-select group built entirely from native HTML — a <fieldset> wrapping same-name <input type="radio"> segments. Grouping, single-select, roving arrow-key navigation, and the accessible name all come from the platform: no ARIA roles and no JavaScript. State is keyed off the nested radio (:has(input:checked)), so it can never desync from the DOM.
Copy the block, give the radios a unique shared name, fill in the <legend>, and mark exactly one segment checked.
<fieldset class="tng-segmented-control"> <legend class="sr-only">Map view</legend> <label class="tng-segmented-control-item"> <input type="radio" name="view" value="list" checked /> <span>List</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="view" value="map" /> <span>Map</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="view" value="grid" /> <span>Grid</span> </label></fieldset>Elements
Section titled “Elements”Group label
Section titled “Group label”The <legend> is the group’s accessible name and is your responsibility to fill in. Keep it visually hidden with sr-only — the track is a flex row, so a visible <legend> would sit inside it as a segment and break the layout; convey the group’s purpose with nearby visible copy instead. Every segment in one control must share the same name, and exactly one must be checked.
Variants
Section titled “Variants”Leading icon
Section titled “Leading icon”A segment can pair a leading .tng-icon with its label. Mark the icon aria-hidden="true" — the label text is the accessible name.
<fieldset class="tng-segmented-control"> <legend class="sr-only">Map view</legend> <label class="tng-segmented-control-item"> <input type="radio" name="view" checked /> <span class="tng-icon icon-list-ul" aria-hidden="true"></span> <span>List</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="view" /> <span class="tng-icon icon-map" aria-hidden="true"></span> <span>Map</span> </label></fieldset>Icon-only
Section titled “Icon-only”Add is-icon for compact square segments with no visible label. The icon is decorative (aria-hidden="true"); give each segment its accessible name with a visually-hidden sr-only span.
<fieldset class="tng-segmented-control is-icon"> <legend class="sr-only">Schedule view</legend> <label class="tng-segmented-control-item"> <input type="radio" name="schedule" checked /> <span class="tng-icon icon-list-ul" aria-hidden="true"></span> <span class="sr-only">List</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="schedule" /> <span class="tng-icon icon-calendar" aria-hidden="true"></span> <span class="sr-only">Calendar</span> </label></fieldset>Three text sizes set the segment typography and padding: is-sm, is-md (default), and is-lg.
<fieldset class="tng-segmented-control is-sm"> <legend class="sr-only">Range</legend> <label class="tng-segmented-control-item"> <input type="radio" name="range-sm" checked /> <span>Day</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="range-sm" /> <span>Week</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="range-sm" /> <span>Month</span> </label></fieldset><fieldset class="tng-segmented-control is-md"> <legend class="sr-only">Range</legend> <label class="tng-segmented-control-item"> <input type="radio" name="range-md" checked /> <span>Day</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="range-md" /> <span>Week</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="range-md" /> <span>Month</span> </label></fieldset><fieldset class="tng-segmented-control is-lg"> <legend class="sr-only">Range</legend> <label class="tng-segmented-control-item"> <input type="radio" name="range-lg" checked /> <span>Day</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="range-lg" /> <span>Week</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="range-lg" /> <span>Month</span> </label></fieldset>States
Section titled “States”Disabled
Section titled “Disabled”Disable a single segment with disabled on its radio, or the whole control with disabled on the <fieldset>. The native disabled attribute removes the affected segments from the tab order and conveys the state to assistive technologies automatically.
<fieldset class="tng-segmented-control"> <legend class="sr-only">View</legend> <label class="tng-segmented-control-item"> <input type="radio" name="view-1" checked /> <span>List</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="view-1" /> <span>Map</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="view-1" disabled /> <span>Grid</span> </label></fieldset><fieldset class="tng-segmented-control" disabled> <legend class="sr-only">View</legend> <label class="tng-segmented-control-item"> <input type="radio" name="view-2" checked /> <span>List</span> </label> <label class="tng-segmented-control-item"> <input type="radio" name="view-2" /> <span>Map</span> </label></fieldset>The segmented control is a labelled radio group — it follows the WAI-ARIA Radio Group pattern and meets WCAG 2.1 AA. Because it’s built from a native <fieldset> and same-name <input type="radio"> segments, the browser provides the grouping, the single-select constraint, roving keyboard navigation, and the selected-state announcement — so the one active choice is clear whether the user sees it, hears it, or tabs to it.
For designers
Section titled “For designers”- Every segment state — rested, hover, selected, disabled, focus — must keep accessible contrast in both the neutral and contrast schemes. Source: WCAG 1.4.3 Contrast (Minimum).
- Never signal the selected segment with colour alone — pair the contained fill with the emphasised label weight so the state reads without relying on hue. Source: WCAG 1.4.1 Use of Color.
- The focus ring must stay visible, meet contrast on every supported surface, and stay clearly distinguishable from both the hover and selected states. Source: WCAG 2.4.7 Focus Visible.
- Use semantic colour tokens (e.g.
foreground/neutral/default) rather than fixed values, so the control adapts correctly across brands and surfaces.
For developers
Section titled “For developers”The segmented control is a native radio group: a <fieldset> wrapping same-name <input type="radio"> segments. Grouping, single-select, roving arrow-key navigation, and the selected-state announcement all come from the platform, so there are no ARIA roles to add and no JavaScript to wire — it satisfies the Radio Group pattern without re-implementing it. Don’t override the native keyboard behaviour, and never rebuild segments from <div> or <button> elements. As the consumer you own the integration contract: supply a meaningful group label and a name unique to the page, start the control with exactly one segment selected, and never suppress the focus ring. Each element’s role, name, and state are in the Labelling elements section below.
Keyboard interaction
Section titled “Keyboard interaction”| Key | Action |
|---|---|
| Tab / Shift + Tab | Move focus into and out of the control, onto the selected segment |
| ← / → / ↑ / ↓ | Move focus and selection between segments |
| Space | Select the focused segment |
The whole segment is the tap target — keep each at least 44 px for comfortable touch use, comfortably clearing the WCAG AA minimum. Source: WCAG 2.5.8 Target Size (Minimum).
Focus order
Section titled “Focus order”The control is a single stop in the tab sequence: focus lands on the selected segment, and the arrow keys then rove between segments within it. The keys that drive this are in the Keyboard interaction table above.
- Focused segment
Source: WCAG 2.4.3 Focus Order.
Labelling elements
Section titled “Labelling elements”Give every element the role, name, and state assistive technology needs.
Segmented control
The native <fieldset> groups the segments and exposes them as a single radio group; its <legend> is the group’s accessible name. Keep the legend sr-only and fill it with a meaningful group name, since a visible legend would break the flex track.
Segment
Each <label> wraps a native <input type="radio">, and the radios share one name so the browser enforces single-select. The label text is the accessible name; for an icon-only segment, supply it with a visually-hidden sr-only span. Selected state is announced from the native checked attribute, and a disabled segment — or a disabled <fieldset> — conveys the disabled state programmatically.
Icon
A leading or icon-only glyph is decorative: mark it aria-hidden="true" so it isn’t announced, and let the label — visible or sr-only — carry the accessible name.
Source: WCAG 4.1.2 Name, Role, Value.