Accordion
An accordion is a stacked set of section headers, each revealing a panel of content
when activated. At its core it is nothing more exotic than a group of
disclosures: every header is a real <button> that toggles the
panel it controls. There is no single native HTML element for the whole pattern, but
the building block — a button that shows and hides a region — needs no ARIA widget
role at all, so prefer that plain, well-understood mechanism over anything heavier.
The widget below follows the WAI-ARIA
Authoring Practices accordion pattern, composed from the same
ewa-disclosure component used elsewhere on this site. The page is a
reference implementation: the markup is authored statically and
disclosure.js enhances it — with scripting off, every panel stays visible
and each button reads as expanded, so the content is always reachable.
Live demo
Tab to any header button and press Enter or Space to toggle its panel. Each section is independent: opening one never closes another, so content a reader has already opened stays put.
An accordion is a vertically stacked group of headers, each of which can reveal or hide an associated panel of content. It lets a page present a lot of sectioned material compactly while letting people open just the parts they need.
Yes. Each panel opens and closes on its own, so you can have several open at once. Forcing only one panel open at a time can yank away content a person was still reading, so this demo keeps them independent.
It degrades gracefully. With scripting off, every panel stays visible and each button reports itself as expanded, so no content is ever trapped behind a control that cannot run.
When to use
Reach for grouped disclosures
- Use an accordion to break a long, clearly sectioned page — an FAQ, settings groups, step-by-step help — into collapsible chunks the reader can scan and expand on demand.
- There is no single native HTML element for the whole pattern, but each section
is just a
<button>toggling a region — no ARIA widget role is needed, so prefer that plain mechanism. - If you only ever have one collapsible section, you want a single disclosure, not an accordion.
- If the content is short enough to show all at once without overwhelming the page, skip the accordion and just show it — hiding readable content behind clicks has a cost.
Markup
Wrap the group in an ewa-accordion, then give each section an
ewa-disclosure marked data-disclosure. The header is a real
<button> inside a heading, and disclosure.js wires
aria-controls, syncs aria-expanded, and hides collapsed
panels.
<div class="ewa-accordion">
<div class="ewa-disclosure" data-disclosure>
<h3>
<button class="ewa-disclosure__trigger" type="button"
aria-expanded="false" aria-controls="acc-p1">
What is an accordion?
</button>
</h3>
<div class="ewa-disclosure__panel" id="acc-p1">
<p>An accordion is a vertically stacked group of headers…</p>
</div>
</div>
<!-- …more <div class="ewa-disclosure" data-disclosure> items… -->
</div>
Keyboard interactions
Because each header is a native <button>, the keyboard model comes
for free — no custom key handling is required.
| Key | Action |
|---|---|
| Tab | Moves focus to the next header button (and on to its panel content when open); Shift + Tab moves backwards. |
| Enter | Toggles the focused section: opens its panel if collapsed, collapses it if open. |
| Space | Toggles the focused section, exactly like Enter — native button behaviour. |
| Any toggle | Affects only the focused section; every other panel keeps its current open or closed state. |
ARIA roles, states, and properties
The accordion needs almost no ARIA: a real button carries the toggle semantics, and a heading lets screen-reader users jump between sections. Only the wiring between button and panel has to be declared.
| Attribute | On | Purpose |
|---|---|---|
<h3> (heading) |
Around each trigger | Wraps the button so screen-reader users can navigate section to section by heading; pick a level that fits the page outline. |
<button> |
Each header | A real button gives focusability and Enter / Space
activation for free — no role or tabindex needed. |
aria-expanded |
Each trigger | true when its panel is open, false when collapsed —
announced as a state change. |
aria-controls |
Each trigger | Points at the id of the panel the button shows and hides. |
hidden |
Collapsed panel | The panel is removed from the accessibility tree and tab order while closed, so collapsed content is genuinely hidden — not just visually clipped. |
Common mistakes
Pitfalls to avoid
- Forcing a single panel open. Auto-closing one section when another opens is an anti-pattern: it can snatch away content a reader was still working through. Keep the disclosures independent — an accordion is just grouped disclosures, nothing more.
- Using a
<div>or link as the header. A real<button>gives focus, keyboard activation, and the right role for free; re-implementing that on a generic element is error-prone and usually incomplete. - Dropping the wrapping heading. Without an
<h3>(or appropriate level) around each button, screen-reader users lose the ability to jump between sections by heading. - Hiding panels with CSS only. Use the
hiddenattribute (ordisplay:none) so collapsed content leaves the tab order and the accessibility tree, rather than staying focusable off-screen. - Signalling open state with colour alone. The
aria-expandedstate — plus a non-colour visual cue such as an icon rotation — must convey open versus closed; a colour swap by itself is not enough.