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.

accordion.html
<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.

Keyboard interactions (each header is a real button)
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.

ARIA attributes and where they live
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 hidden attribute (or display: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-expanded state — plus a non-colour visual cue such as an icon rotation — must convey open versus closed; a colour swap by itself is not enough.