Accessibility Certified – Home

Accordion: expand and collapse without locking anyone out

Accordions save space in FAQs and long pages. For everyone to use them, each heading needs a real button that tells assistive technology whether it is open.

4.1.22.1.11.3.12.4.72.4.6

Live demo

This demo is checked with axe-core without violations and fully operable by keyboard.

Try it with the keyboard: Tab, Enter, Space, the arrow keys and Escape. With a screen reader on, you hear what gets announced.

Your home page, automatically: contrast, labels, headings, keyboard operation and more. You get a score and the three most important issues.

The live demo needs JavaScript. Code and requirements are below.

Keyboard interaction

Tab
Moves to the next accordion header or into the open panel.
Enter / Space
Opens or closes the section.

When to use this pattern

Use an accordion when visitors only need some sections, such as frequently asked questions. For a single expandable area the native <details> and <summary> elements are often enough: they work without JavaScript and expose their state themselves. A custom accordion is worth it when you need full control over heading structure and design.

Do not hide essential information such as prices or deadlines in collapsed sections that people easily miss.

Roles, states and properties

  • Each header is a <button> inside a heading (<h3> or similar), so the page outline stays intact for screen reader users.
  • aria-expanded="true" or "false" on the button tells whether the section is open.
  • aria-controls points to the id of the content panel.
  • Collapsed panels use hidden, so they disappear for screen readers and from the tab order too.
  • Optional: role="region" with aria-labelledby pointing at the button, but only for a few panels (up to about six); otherwise there are too many landmarks.

Keyboard interaction

KeyEffect
TabMoves to the next accordion header or into the open panel.
Enter / SpaceOpens or closes the section.

Code

HTML and JavaScript (shortened)
<h3>
  <button type="button" aria-expanded="false"
    aria-controls="faq-1" id="faq-1-btn">
    How long does an audit take?
  </button>
</h3>
<div id="faq-1" role="region"
  aria-labelledby="faq-1-btn" hidden>
  <p>Usually a few working days.</p>
</div>

<script type="module">
for (const btn of document.querySelectorAll('[aria-controls]')) {
  btn.addEventListener('click', () => {
    const open = btn.getAttribute('aria-expanded') === 'true';
    btn.setAttribute('aria-expanded', String(!open));
    document.getElementById(
      btn.getAttribute('aria-controls')).hidden = open;
  });
}
</script>

Common mistakes

  • A clickable <div> instead of a button: not reachable by keyboard, no role, no state.
  • State shown only by a plus or chevron icon, without aria-expanded.
  • Content hidden with height: 0 only: links inside stay in the tab order and are read out.
  • The header is a heading without a button, or the button sits outside the heading, leaving gaps in the heading list.

Related WCAG criteria

Sources

Are your components built like this?

The free check finds many issues automatically. In the audit a person tests every component with a keyboard and a screen reader.

Free checkSee our method