Accessibility
The Tab Picker is a radio group, not a tablist — it follows the WAI-ARIA Radio Group pattern and meets WCAG 2.1 AA. It borrows the tab strip’s visual from Tabs, but nothing about its accessibility comes from that resemblance: an accessible picker announces itself as a named group of mutually exclusive options, exposes exactly one of them as checked, and stays fully operable from the keyboard — so the current choice is clear whether the user is looking at it, listening to it, or tabbing through it.
For designers
Section titled “For designers”- All states (rested, hover, active, focus, and disabled) must maintain accessible contrast on every supported surface. Source: WCAG 1.4.3 Contrast (Minimum).
- Selection must never rely on colour alone — reinforce it with the emphasised label weight and the active border, so it survives for colour-blind users. Source: WCAG 1.4.1 Use of Color.
- Focus styles must stay visible, meet contrast requirements on every supported surface, and be clearly distinguishable from hover and from the selected state. Source: WCAG 2.4.7 Focus Visible.
- Disabled options must stay readable while clearly reading as unavailable.
- Use semantic colour tokens (e.g.
foreground/neutral/default) rather than fixed values, so the component adapts correctly across themes and surfaces.
For developers
Section titled “For developers”Build the picker from a native <fieldset> of <input type="radio"> elements sharing one name. It needs no ARIA and no JS: single-select, roving focus, and the group itself all come from the browser. Do not add role="tablist", role="tab", or role="radiogroup" — the native fieldset and radios already expose the group, and a tablist role would promise content panels the picker doesn’t have. Each radio is visually hidden but stays focusable and present in the accessibility tree, so it announces as “radio button, n of m, group name”.
The per-element roles, names, and states are in the Labelling elements section below.
Keyboard interaction
Section titled “Keyboard interaction”The picker is fully operable from the keyboard, and every key below is native radio-group behaviour rather than something the consumer wires up. Source: WCAG 2.1.1 Keyboard.
| Key | Action |
|---|---|
| Tab | Move focus into the group (the checked option), then out again |
| Shift + Tab | Move focus to the previous interactive element |
| ← / ↑ | Move to and select the previous option |
| → / ↓ | Move to and select the next option |
| Space | Select the focused option |
Because the arrow keys select as they move, a picker should never be wired so that changing the selection navigates or submits — that would strand keyboard users mid-row.
Interactive targets must be large enough 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 whole group is a single stop in the page’s tab sequence: Tab lands on the checked option (or the first option when none is checked), and a further Tab leaves the group entirely — the arrow keys rove between options from there. The keys that drive this are in the Keyboard interaction table above.
- Focused option
Source: WCAG 2.4.3 Focus Order.
Labelling elements
Section titled “Labelling elements”Give every element the role, name, and state assistive technology needs.
Group
The native <fieldset> exposes the radio group — no role is needed or wanted. Its <legend> is the group’s accessible name, and it is required even though the design shows no visible label: hide it with sr-only rather than dropping it, or the options announce with no indication of what they belong to.
Option
Each <input type="radio"> takes its accessible name from the <label> text wrapping it, so no aria-label is needed — keep labels short and self-describing. All options in one picker must share a single name attribute; that attribute is what makes them mutually exclusive.
Selected state
The checked radio is the selection, exposed through native :checked rather than aria-selected or aria-current. Ship exactly one option checked in the initial markup so the group never announces as having no value.
Disabled option
Disable the nested <input> with the native disabled attribute, not the <label> and not an .is-disabled class. The browser then removes the option from the group’s arrow-key roving and announces it as unavailable.
Source: WCAG 4.1.2 Name, Role, Value.