AccessScanRun a free scan

Guide

Accessible Tabs and Accordion Patterns: ARIA, Keyboard, and Native details/summary

Tabs and accordions are two of the most over-built, under-tested widgets on the web. Both hide content behind a control the user activates to reveal it, which is exactly why so many implementations break for keyboard and screen reader users. The good news: the WAI-ARIA Authoring Practices Guide (APG) defines precise roles and keyboard behavior for each, and for accordions there is often a zero-JavaScript native option.

This guide shows how to build accessible tabs and accordions correctly, from ARIA roles to the arrow-key behavior screen reader users expect, plus the cases where native details and summary save you the work entirely.

Tabs and accordions are not the same widget

They look similar but solve different problems, and the APG treats them as distinct patterns. A tab widget shows one panel at a time from a set of mutually exclusive choices, and the whole tab list is a single stop in the page tab order. An accordion is a vertical stack of headers where each header expands or collapses its own section, and any number of sections can be open at once (or only one, if you enforce it).

Picking the wrong pattern produces the wrong keyboard model. Decide first: is the user choosing between alternatives that occupy the same space (tabs), or scanning a long page broken into collapsible chunks (accordion)? A frequent mistake is calling a stack of expandable panels tabs and wiring up role="tab". If sections can be open simultaneously and each has its own toggle, it is an accordion, and the markup follows from there.

Correct ARIA roles for tabs

The tab pattern needs three roles working together. A container with role="tablist" wraps the tabs. Each clickable label is a role="tab" element. Each content region is a role="tabpanel". Wire them together with these attributes:

  • Every tab has aria-controls pointing to the id of its panel, and aria-selected="true" on the active tab (false on the rest).
  • Each tabpanel has aria-labelledby pointing back to the id of its tab, so the panel is named by its tab.
  • Only the active tab has tabindex="0"; inactive tabs get tabindex="-1" so they stay reachable by arrow keys but not by Tab.
  • If the tablist is meaningful as a group, add aria-label or aria-labelledby on the tablist element itself.

Build tabs from real <button> elements inside the tablist. Buttons give you focusability, Enter and Space activation, and a clear accessible role for free. Never hang role="tab" on a <div> and reinvent what a button already does, a recurring theme in our ARIA best practices guide.

Keyboard interaction the APG requires for tabs

This is where most homegrown widgets fail. The APG specifies a roving tabindex: one Tab press moves focus into the active tab, and arrow keys move between tabs without leaving the tablist.

  • Left/Right arrows move between tabs in a horizontal tablist (Up/Down for a vertical one, where you also set aria-orientation="vertical").
  • Home moves to the first tab, End to the last.
  • Tab from a focused tab moves out of the tablist and into the active tabpanel, not to the next tab.
  • Enter or Space activates a tab when you use manual activation.

You choose between automatic and manual activation. With automatic activation, simply moving focus with an arrow key selects and shows that panel; this is fine when panels are cheap to render. With manual activation, arrows move focus but the user must press Enter or Space to switch panels, which is safer when switching is expensive or panels load data. If a tabpanel contains no focusable elements, give the panel itself tabindex="0" so keyboard users can scroll and read it.

Correct ARIA and keyboard for accordions

Accordions are simpler because each header is just a disclosure button. The accessible structure: wrap each section title in a real heading element (<h3> or whatever fits the document outline), and put a <button> inside that heading. The button carries aria-expanded="true" or "false" and aria-controls pointing to the id of the region it toggles. The collapsible region is labelled by its button via aria-labelledby; role="region" is appropriate when the section is significant enough to act as a landmark.

The keyboard model is mostly native. Because each header is a button, Enter and Space toggle it with no extra code, and Tab moves through headers in order. The APG lists optional Up/Down arrow, Home, and End support to move between accordion headers, but unlike tabs this is not required, manual Tab navigation already works. Crucially, do not trap Tab inside an open section: focus should flow naturally from the header into the revealed content and onward.

Toggle visibility with hidden or display:none, not just visual clipping. Content that is visually hidden but still in the accessibility tree confuses screen reader users, who hear collapsed content as if it were on the page. The same discipline keeps keyboard focus from landing on controls that are not actually visible.

When to use native details and summary instead

For a simple, independently collapsible section, the native <details> and <summary> elements give you an accessible disclosure widget with no JavaScript and no ARIA. The browser handles expanded state, button semantics, Enter and Space activation, and focus. A FAQ list, a row of optional details, or a single show-more block are ideal candidates.

Reach for the ARIA accordion pattern instead when you need behavior the native element does not give you: a single-open (exclusive) accordion where opening one section closes the others, animated height transitions that play reliably across browsers, or arrow-key navigation between headers. You can group several <details> with the same name attribute to get exclusive-open behavior natively in modern browsers, but support and animation control are still uneven, so test before relying on it. Verify whichever path you choose with real screen reader testing rather than trusting the markup alone.

Why this matters for compliance

Broken tabs and accordions map directly to failures under WCAG 2.2, the baseline behind the European Accessibility Act (its requirements apply from 28 June 2025, with conformance assessed via EN 301 549) and the de-facto standard in ADA settlements. Keyboard traps and unreachable panels fail 2.1.1 Keyboard. Missing or wrong aria-expanded and aria-selected fail 4.1.2 Name, Role, Value. Under WCAG 2.2, the toggle controls should also meet 2.5.8 Target Size (Minimum) of 24 by 24 CSS pixels, unless an exception such as the spacing or inline exception applies, and the focused tab or header must not be hidden behind a sticky bar (2.4.11 Focus Not Obscured).

You can catch many of these issues automatically. Run your component through AccessScan to flag missing roles, broken aria-controls references, and focus problems, then confirm the keyboard flow by hand. For a structured pass across your whole site, work through our accessibility checklist.

Check your site against AccessScan

See your issues ranked by impact in seconds — free.

Run a free accessibility scan

FAQ

Should tabs use automatic or manual activation?

Use automatic activation when showing a panel is cheap and instant, so arrow keys both move focus and reveal the panel. Use manual activation (arrow to move focus, Enter or Space to activate) when switching panels is expensive or loads data, to avoid firing work on every arrow press.

Do accordion headers need arrow-key navigation?

No. The APG lists Up/Down, Home, and End between accordion headers as optional. Because each header is a real button, Tab and Enter/Space already provide full keyboard access. Arrow keys are required for tabs, not accordions.

Is details/summary enough for an accessible accordion?

For independent collapsible sections, yes, native details and summary are accessible with zero JavaScript. Switch to the ARIA accordion pattern when you need exclusive single-open behavior, cross-browser animation control, or arrow-key navigation between headers.

What is the most common tabs accessibility bug?

Making every tab a normal Tab stop instead of using a roving tabindex. The correct model is one Tab into the active tab, arrow keys to move between tabs, and Tab again to move into the panel.

More guides