# An accordion opens without exposing its state

Source: https://easeweb.dev/learn/accordion-controls
Topics: Tabs, accordions and disclosures > Accordions expose open and closed state (WCAG 4.1.2)
The fix: Use details/summary for a simple disclosure, or a button inside a heading for a custom accordion. Synchronize expanded state and actual panel visibility.
Test with: keyboard, screen reader, zoom, forced colors
References: https://www.w3.org/WAI/ARIA/apg/patterns/accordion/

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

An FAQ heading responds to clicks. Its answer appears below, but the heading is plain text in the accessibility tree and keyboard users cannot toggle it.

## Why it fails

The heading acts as a control without button semantics. The expanded state is not exposed, and hidden panel links may remain in the tab order.

## Who is affected

A user cannot reliably discover which headings are interactive or whether a panel is open. Invisible tab stops interrupt navigation.

## What automation and AI miss

Automation can detect some missing attributes, but it cannot establish that `aria-expanded` matches every state transition or that hidden descendants stop receiving focus. Test repeated opening and closing.

## Before: a clickable heading and a squeezed panel

```html
<h3 class="faq-q" onclick="toggle(this)">How do refunds work?</h3>
<div class="faq-a" style="height: 0; overflow: hidden">
  <p>Refunds arrive within 5 days. <a href="/refunds">Refund policy</a></p>
</div>
```

The heading cannot be reached with Tab and says nothing about its state. The closed panel is only squeezed to zero height, so its link still takes focus where nobody can see it.

## After: a native disclosure

For a simple question and answer, `details` and `summary` give you keyboard access, the open and closed state, and hidden content out of the tab order, with no script:

```html
<details>
  <summary>How do refunds work?</summary>
  <p>Refunds arrive within 5 days. <a href="/refunds">Refund policy</a></p>
</details>
```

## After: a custom accordion

When the design needs more control, put a real button inside the heading and keep its state in step with the panel:

```html
<h3>
  <button type="button" aria-expanded="false" aria-controls="faq-refunds">
    How do refunds work?
  </button>
</h3>
<div id="faq-refunds" hidden>
  <p>Refunds arrive within 5 days. <a href="/refunds">Refund policy</a></p>
</div>
```

```js
for (const button of document.querySelectorAll('[aria-controls]')) {
  button.addEventListener('click', () => {
    const open = button.getAttribute('aria-expanded') === 'true';
    button.setAttribute('aria-expanded', String(!open));
    document.getElementById(button.getAttribute('aria-controls')).hidden = open;
  });
}
```

The `hidden` attribute removes the closed panel from the tab order and from what screen readers can reach. If you animate the panel, make sure it ends with `hidden` set, not just zero height.

## Implementation decisions

Avoid making the entire heading and a separate icon two different toggle controls. A native disclosure usually needs no extra `aria-expanded`. For custom controls, use stable IDs with `aria-controls`, and decide whether several panels may stay open. Arrow-key shortcuts are optional; Tab access must remain usable.

Keep the heading level that fits the page outline, so the questions still work as headings for screen-reader navigation.

## Verify the fix

1. Reach each question with Tab and open and close it with Enter and Space.
2. Confirm the announced state changes when the panel opens and closes.
3. Close a panel containing links and check that those links no longer receive focus.
4. Test several panels, and check that the headings still form a meaningful outline.
5. Check the focus indicator and the open and closed icons at increased zoom and in forced colors.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the shared accordion component changes.

## Limits

This is a starting pattern, not a guarantee for every application. The script is a minimal sketch without animation or deep links. Test the complete journey with your target assistive technology.
