What the tabs pattern is
A tabs widget is a row of activator controls (the tablist) where each activator (a tab) reveals a corresponding tabpanel of content. Only one panel is visible at a time. The pattern is one of the most-used composite widgets on the web — about 14% of unique pages on the HTTP Archive 2024 report include a tabs pattern.
<div role="tablist" aria-label="Settings">
<button role="tab" id="tab-1" aria-selected="true" aria-controls="panel-1" tabindex="0">Profile</button>
<button role="tab" id="tab-2" aria-selected="false" aria-controls="panel-2" tabindex="-1">Notifications</button>
<button role="tab" id="tab-3" aria-selected="false" aria-controls="panel-3" tabindex="-1">Privacy</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">…</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>…</div>
<div role="tabpanel" id="panel-3" aria-labelledby="tab-3" hidden>…</div>
Key requirements:
role="tablist"on the container; anaria-labeloraria-labelledbyif multiple tablists exist on the page.role="tab"on each activator;aria-selected="true"on the current tab,"false"on others.aria-controlson each tab pointing to the panel id.- Roving
tabindex: only the current tab hastabindex="0"; others havetabindex="-1". role="tabpanel"on each panel;aria-labelledbyreferencing the tab id; non-current panels hidden via thehiddenattribute (preferred) ordisplay: none.
Manual vs automatic activation
Two valid sub-patterns differ in when the panel changes:
| Sub-pattern | Trigger to switch panel | Keyboard semantics |
|---|---|---|
| Automatic | Arrow-key alone | Up/Down (vertical) or Left/Right (horizontal) navigates and activates. |
| Manual | Arrow-key + Enter or Space | Arrow-key moves focus; Enter / Space activates. |
The APG recommends automatic when each tab’s panel is cheap to render (e.g., already in the DOM). It recommends manual when the panel is heavy to render or fetches data — automatic activation would trigger a network request on every arrow key.
About 65% of production tabs widgets surveyed in 2024 use automatic; large dashboards (Datadog, Grafana, etc.) use manual.
Keyboard interaction
| Key | Effect (automatic) | Effect (manual) |
|---|---|---|
| Tab | Move focus to the active tab; if focus already on the tablist, leave to next focusable. | Same. |
| Right Arrow | Activate next tab; wrap to first if at end. | Move focus to next tab. |
| Left Arrow | Activate previous tab; wrap to last if at start. | Move focus to previous tab. |
| Home | Activate / focus first tab. | Same. |
| End | Activate / focus last tab. | Same. |
| Enter / Space | (no effect; arrow already activated) | Activate the focused tab. |
For vertical tablists, swap Up/Down for Left/Right.
Common pitfalls
- No roving tabindex. Every tab
tabindex="0"; Tab walks through all of them, breaking the keyboard model. aria-selectednot synced withtabindex. Selected tab must be the onlytabindex="0".- Tab panels in the DOM but visually hidden via opacity. The
hidden attribute (or
display: none) is required so AT does not announce hidden content. - Heavy panel content rendered automatically. Use manual activation, or load the panel lazily on first activation.
<a href>instead of<button>for tabs. Permitted but the link follows on click in some engines, navigating away.- Multiple tablists without
aria-label. Two tablists with identical “tab 1, tab 2” announcements confuse screen-reader users.
Cross-engine support
The pattern is implemented at the markup layer; engines just have
to expose role="tab" and friends to AX. Mapping pass-rate is
about 99% across Chromium (Blink), WebKit, and Gecko per
wpt.fyi/wai-aria as of 2024.
Authoring tips
- Wire focus management early. Most tab bugs are focus-management bugs disguised as ARIA bugs.
- Persist the active tab in the URL hash for deep links:
#panel=privacy. The pattern survives a refresh. - For tablists that overflow horizontally on narrow viewports,
ensure arrow-key navigation still works inside an
overflow-x: autocontainer; auto-scroll the active tab into view withscrollIntoView({ inline: 'nearest' }). - Avoid mixing tabs and accordions in the same component: the responsive transformation breaks the ARIA model. Prefer two separate components selected by media query.
Further reading
- WAI-ARIA Authoring Practices, Tabs Pattern.
- WAI-ARIA 1.2, §5.3.1 Composite roles.
- Adrian Roselli’s Tabs accessibility deep-dive surveys the manual-vs-automatic decision.
- The Open UI tabs research
is the long-term direction toward a native
<tabs>element.