ARIA Hidden On A Focusable Element
ID |
javascript.jsx_aria_hidden_focusable |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
JavaScript |
Tags |
accessibility, jsx, reliability |
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 trap 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" /> // FLAW - tabIndex puts it back in the order
<button aria-hidden="true" tabIndex={-1}>Save</button> // OK - hidden and out of the order
<span aria-hidden="true">x</span> // OK - decoration, takes no focus
<button aria-hidden={collapsed}>Save</button> // OK - computed, not judged here
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.
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.