ARIA Hidden On A Focusable Element

ID

html.aria_hidden_focusable

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Html

Tags

accessibility, reliability

Description

Flags aria-hidden="true" on an element that still takes keyboard focus.

Rationale

aria-hidden removes an element from the accessibility tree but not from the tab order. A focusable element hidden this way becomes a focus stop with nothing to announce: the user tabs onto it, the screen reader has no node to read, and the keyboard focus appears to vanish. This is the one combination the ARIA specification calls out as never valid.

<button aria-hidden="true">Save</button>              <!-- FLAW — reachable by Tab, unreadable there -->
<div tabindex="0" aria-hidden="true">x</div>          <!-- FLAW — tabindex puts it back in the order -->
<a href="/help" aria-hidden="true">Help</a>           <!-- FLAW — a link with an href is focusable -->

<button aria-hidden="true" tabindex="-1">Save</button>  <!-- OK — hidden and out of the order -->
<span aria-hidden="true">&times;</span>                 <!-- OK — decoration, takes no focus -->
<a aria-hidden="true">No href</a>                       <!-- OK — not a link without one -->
<button aria-hidden="${collapsed}">Save</button>        <!-- OK — settled at render time -->

Focusability is read as written, and the tabindex decides when there is one: a negative tabindex takes even a button out of the tab order — which is exactly the fix — while a non-negative one puts any element into it. With no tabindex, the tag decides: the interactive controls, the links that carry an href, and the embedded-content tags.

Only a literal true is reported. A valueless aria-hidden, or an empty one, is the property’s undefined state and does not hide the element at all, so it is not a finding.

Remediation

Add tabindex="-1" so the element leaves the tab order along with the accessibility tree, or drop aria-hidden and hide the element with display: none or the hidden attribute, both of which remove it from both. If the element has to stay operable, give it an accessible name instead of hiding it.

© 2026 Xygeni. All rights reserved