Standards · ARIA

Property Relationship

aria-controls

Names the element or elements whose presence or content this control governs. Common pairings: a tab controls its tabpanel, a disclosure button controls a revealed region. Support across assistive tech is uneven — use sparingly.

When to use

On a control whose activation changes a different element on the page — the canonical examples are the tab → tabpanel relationship and a disclosure button → revealed panel. It tells assistive tech that “operating this widget will affect that one over there”, giving advanced users a way to jump from the control to the controlled element.

Do not reach for it on every interaction. Most click handlers do not need aria-controls; reserve it for cases where the controlled element is genuinely separate in the DOM and the user benefits from knowing the connection.

How it behaves

The value is a space-separated list of element IDs for the controlled element(s). The controlled element must exist in the DOM at the time the relationship is queried — if you mount the tabpanel only when the tab is activated, the reference dangles for sighted-AT users until the panel appears.

Support is the soft spot. JAWS exposes a “go to controlled element” shortcut, but VoiceOver and NVDA largely ignore the attribute outside specific patterns (tabs, comboboxes). That means aria-controls is rarely the thing that makes a feature accessible — it is an enhancement on top of correct roles, focus management, and aria-expanded. Build the pattern right first; add aria-controls last.

Common failures

  • Treating aria-controls as a substitute for moving focus. A “Skip to results” button needs to move focus on activation; adding aria-controls does not do that.
  • Pointing at an element that is not yet in the DOM. The reference resolves to nothing until the panel renders.
  • Forgetting to also set aria-expanded on a disclosure button. aria-controls says “I influence that thing”; aria-expanded says “and it is currently open / closed”.
  • Using aria-controls on a custom select to point at an option list — the right pattern there is role="combobox" plus aria-controls on the input, with aria-activedescendant for the highlighted option.
  • Sprinkling aria-controls on every button “for completeness”. Noise; nothing relies on it.

Example

<button
  type="button"
  aria-expanded="false"
  aria-controls="faq-1-answer"
>
  What is your refund policy?
</button>
<div id="faq-1-answer" hidden>
  Refunds are processed within 14 days …
</div>