( Robust / WCAG 4.1.2 )

Focusable element is inside an aria-hidden container

CriticalLevel AWCAG 4.1.2 — Name, Role, Value

What is this issue?

An element with aria-hidden="true" (correctly scoped, not applied to body) contains a descendant that’s natively focusable or has been given tabindex="0", and that descendant remains part of the page’s keyboard tab order despite living inside a hidden ancestor. aria-hidden and keyboard focusability are governed by two separate mechanisms in the browser: one controls what the accessibility tree exposes, the other controls what Tab can reach, and this rule catches the gap where the two disagree.

The most common source is a closed dropdown, an off-canvas navigation drawer, or a collapsed accordion panel: the container correctly gets aria-hidden="true" the moment it closes, but the links and buttons inside it were never also disabled from receiving focus, so they’re still sitting in the tab order exactly as if the panel were open and visible.

Why does this matter?

aria-hidden="true" removes an element and everything inside it from the accessibility tree, meaning a screen reader treats that content as if it doesn’t exist: it’s skipped entirely during normal reading and navigation. But keyboard focus order is a separate system, controlled by tabindex and native element focusability, and aria-hidden doesn’t automatically change either one.

The result is two different, equally broken experiences depending on which assistive technology a user relies on. A sighted keyboard-only user tabbing through the page can land on a control inside a visually or accessibility-hidden region and lose track of where their focus indicator has gone, since the surrounding content may be off-screen or invisible. A screen reader user tabbing to the same element gets something stranger: their screen reader announces nothing at all, because the element they’ve just focused is, as far as the accessibility tree is concerned, not there: focus has moved to something that doesn’t exist from their perspective.

Who is affected?

  • Screen reader users: hear complete silence when focus lands on the hidden-but-focusable element, since aria-hidden removes it from what the screen reader can announce, even though focus genuinely moved there.
  • Keyboard users: can tab into a region that’s visually hidden or off-screen and lose track of their focus position entirely, with no visible indicator to follow since the container they’ve landed inside isn’t meant to be seen.

What users experience

Jamal is a keyboard-only user with a motor impairment who navigates a retail site’s mobile-style navigation menu using Tab, since he can’t operate a mouse. The site’s off-canvas menu closes by adding aria-hidden="true" to its container, but the menu’s links never received tabindex="-1" when it closes. After dismissing the menu, Jamal continues tabbing through the page and, five tabs later, his focus silently lands on a “Track your order” link still sitting inside the now-hidden, off-screen menu. No visible focus outline appears anywhere on his screen, because the element he’s landed on isn’t rendered in the current viewport at all. He has to guess and press Tab repeatedly, hoping to tab back into visible content, since there’s no way to see where his own keyboard focus currently is.

How do I fix it?

Add tabindex="-1" to every naturally-focusable element inside a container the moment it becomes aria-hidden="true", and remove that tabindex="-1" again when the container becomes visible. This works because tabindex="-1" takes an element out of the natural Tab sequence without removing it from the DOM, so a closed drawer’s links stop being keyboard-reachable in exactly the same moment they stop being visible and screen-reader-reachable.

For most modern browsers, the inert HTML attribute is a simpler, single-attribute alternative: applying inert to the container removes it from both the accessibility tree and the keyboard tab order in one step, without needing to manage tabindex on each individual descendant. Whichever approach you use, also make sure focus is moved out of the region before it’s hidden if a user’s focus happens to currently be inside it. Hiding a region a user is actively focused in, without moving focus first, strands them exactly the way this rule describes.

Code Examples

Before
<nav class="mobile-menu" aria-hidden="true">
  <a href="/account">My account</a>
  <a href="/orders">Track your order</a>
</nav>
After
<!-- Method 1: tabindex="-1" on every focusable descendant -->
<nav class="mobile-menu" aria-hidden="true">
  <a href="/account" tabindex="-1">My account</a>
  <a href="/orders" tabindex="-1">Track your order</a>
</nav>

<!-- Method 2: inert removes accessibility AND focus in one attribute -->
<nav class="mobile-menu" inert>
  <a href="/account">My account</a>
  <a href="/orders">Track your order</a>
</nav>

Method 1 keeps aria-hidden as the mechanism controlling accessibility-tree visibility and adds tabindex="-1" on each link to separately remove it from the tab order, which requires toggling both attributes together every time the menu opens or closes. Method 2 replaces both jobs with the single inert attribute, which handles accessibility-tree removal and focus removal as one unit, simpler to maintain correctly, provided the target browsers you support have inert available.

Common Mistakes

Mistake: “aria-hidden already means the element is invisible, so it can’t receive focus.” aria-hidden only controls the accessibility tree, not visual rendering or keyboard focusability; those are separate CSS and tabindex mechanisms entirely. An element can be simultaneously aria-hidden="true", visually rendered off-screen with CSS, and still fully focusable via Tab unless something explicitly removes it from the tab order too.

Mistake: “I set display:none on the container, so this can’t apply.” display: none genuinely does remove an element from both the accessibility tree and the tab order, so a container hidden that way is not vulnerable to this specific bug. The failure mode this rule catches is specifically containers hidden with aria-hidden (or with CSS like visibility: hidden combined with aria-hidden, or off-screen positioning) rather than display: none, where the browser’s automatic focus-removal doesn’t kick in.

Mistake: “This only matters for screen reader users, so it’s a lower-priority accessibility issue.” This bug produces a distinct, separate failure for keyboard-only users too (losing all visible focus indication when Tab lands inside hidden or off-screen content), independent of whether a screen reader is running at all. Framing it as screen-reader-only understates half of who it actually breaks the experience for.

How RedFlag Detects This

Automated: axe-core rule, runs on every scan. RedFlag calls axe-core’s aria-hidden-focus rule as part of every scan, restricted to the WCAG 2.0/2.1/2.2 A and AA rule set. The rule finds every element with aria-hidden="true" (excluding body, covered by a separate check) and inspects its descendants for anything natively focusable or carrying tabindex="0" or higher, flagging the container when a reachable focusable descendant is found.

False negative: the rule evaluates the page at scan time; a container that only becomes aria-hidden after a specific user interaction, like closing a menu, and never gets scanned in that closed state, won’t be flagged even if the same underlying bug exists. False positive: none typical for this check, since detecting a focusable descendant inside a hidden ancestor is a structural DOM condition axe-core evaluates reliably. Manual step: trigger every collapsible, closeable, or dismissible region on the page into its hidden state and Tab through the surrounding content, confirming keyboard focus never silently lands inside it.

Manual Testing

  1. Open the page in Chrome or Firefox with NVDA or VoiceOver running, and also test with the screen reader off, using only the keyboard.
  2. Close, collapse, or dismiss every dropdown, drawer, or accordion panel on the page.
  3. Starting from before the hidden region in the page’s tab order, press Tab repeatedly and count how many tab stops it takes to pass through where the hidden region sits.
  4. If the number of tab stops matches the number of interactive elements the hidden region contains, focus is still landing inside it. With the screen reader running, listen for a screen reader announcement to confirm; expect silence if the bug is present.
  5. Open the browser’s accessibility inspector (Chrome DevTools → Elements → Accessibility pane) and confirm each interactive element inside the hidden container shows as not focusable (tabindex="-1" or an inert ancestor) once the container is in its hidden state.

4.1.2 Name, Role, Value: Every interface component must expose a consistent name, role, and value to assistive technology. A focusable element trapped inside an aria-hidden container fails this criterion in an unusual way: focus genuinely moves to the element, but the element itself has no accessible presence to describe once it’s reached.

aria-hidden=“true” is present on the document body is the more severe, related misuse of the same aria-hidden attribute: the entire page hidden at once, rather than a correctly-scoped container that still leaks one focusable element.

ARIA element is missing a required parent role commonly appears in the same off-canvas menu or dropdown component, since both issues tend to surface in custom navigation and disclosure widgets built without following an established interaction pattern.

ARIA attribute has an invalid value and ARIA attribute name is not valid both concern the same aria-hidden attribute’s spelling and value correctness: a container can pass both of those checks and still fail this one if the value is valid but the focus management around it is incomplete.

ARIA attribute is not valid for the current role value shares this rule’s underlying theme of two independent mechanisms (structural context there, focus order here) needing to agree with an ARIA attribute’s stated intent for it to actually work.

References

Frequently asked questions

Why does aria-hidden not also remove an element from the keyboard tab order?

Because aria-hidden and tab order are controlled by two entirely separate mechanisms. aria-hidden only affects what the accessibility tree exposes to assistive technology; the browser's focus order is governed by tabindex and each element's natural focusability, which aria-hidden does not touch on its own.

Does the inert attribute solve this problem better than aria-hidden alone?

Yes, for hidden interactive regions specifically. The inert HTML attribute removes an element from both the accessibility tree and the keyboard tab order in one step, which is exactly the combined behavior a hidden, closed drawer or off-screen panel needs and aria-hidden alone does not provide.

Is this the same underlying attribute as the aria-hidden-body rule?

Yes, both rules check misuse of the same aria-hidden attribute, on different scopes. aria-hidden-body checks the single, most severe case of the attribute landing on the document body. This rule checks the more common, narrower case of a correctly-scoped hidden container that still contains a reachable, focusable element inside it.

Does this bug usually happen on purpose, or is it always accidental?

Almost always accidental. It typically shows up in a closed dropdown, an off-canvas navigation drawer, or a collapsed accordion panel where the container is marked aria-hidden="true" (or simply positioned off-screen) as soon as it closes, but the interactive elements inside it, like links and buttons, were never also given tabindex="-1" or covered by an inert wrapper.

What should happen to focus if a user is already inside the region when it becomes hidden?

Focus should be moved somewhere else in the same action that hides the region, typically back to the control that triggered it (like the hamburger menu button that opened a now-closing drawer). Leaving focus stranded inside newly-hidden content creates the exact silent, disorienting gap this rule exists to catch.