axe-core
axe-core copied to clipboard
False positive in aria-hidden focusable rule
False positive aria-hidden focusable failure is being reported on focus "bumpers", which are meant to send focus back into the modal/focus trap zone but never receive focus themselves. These elements have aria-hidden="true" so those elements are not announced as clickable/interactive elements to screen reader users. This issue seems to be related to https://github.com/dequelabs/axe-core/pull/3407, but not fixed by that PR.
Codepen example repro: https://codepen.io/pankhurinigam/pen/BawPRbQ
Product: axe-core
Expectation: No aria-hidden failures reported on elements that are not meant to be focusable.
Actual: Failures reported.
Motivation: False positive is confusing to users.
Thanks for the issue. This one might be tough to fix, but I have an idea that might work out.
Edit: For future reference: if the element in question has 0 width/height and/or pointer-events: none, we should move it to needs review instead of fail
Like @WilcoFiers mentioned a few weeks ago in #3406, the gold standard would be to be able to base the "treat as incomplete" decision off of whether there's an on focus event listener registered; it's a shame that it's technically infeasible without the stalled https://github.com/whatwg/dom/issues/412 proposal :(
Validated with the latest axe-core develop branch code base,
for the below test scripts: aria-hidden-focus rule presenting incomplete results for the,
<!--Incomplete examples-->
<div aria-hidden="true" id="target">
<button onfocus="redirectFocus()">button</button>
</div>
<!--returns undefined when all focusable controls have onfocus event-->
<div aria-hidden="true" id="target">
<a href="/" onfocus="redirectFocus()">First link</a>
<a href="/" onfocus="redirectFocus()">Second link</a>
</div>
<!--returns false when some, but not all focusable controls have onfocus events-->
<div aria-hidden="true" id="target">
<a href="/" onfocus="redirectFocus()">First link</a>
<a href="/"">Second link</a>
</div>
<!--returns undefined when control has 0 width and height and pointer events: none (focus trap bumper-->
<div id="target" aria-hidden="true" tabindex="0" style="pointer-events: none"></div>
<!--returns undefined when control has 0 width and height and pointer events: none (focus trap bumper) -->
<button id="target" aria-hidden="true" style="pointer-events: none; width: 0; height: 0; margin: 0; padding: 0; border: 0"></button>'
failing at the scripts:
<!--failure examples-->
<div aria-hidden="true">
<a href="/" style="position:absolute; top:-999em" onfocus="document.querySelector('input').focus()">First link</a>
</div>
<input />
<fieldset id="target" disabled aria-hidden="true"><legend><input /></legend></fieldset>'
<fieldset id="target" aria-hidden="true"><input /></fieldset>
<div id="target" aria-hidden="true"><a href="/" style="position:absolute; top:-999em">Link</a></div>'
<div id="target" aria-hidden="true"><input type="text" aria-disabled="true"/></div>
<div aria-hidden="true"><a id="target" href="">foo</a><button>bar</button></div>'
also validated for the pass scripts:
<!--Pass examples-->
<div aria-hidden="true"></div>
<button tabindex="-1">Some button</button>
</div>
<p id="target" aria-hidden="true">Some text</p>
<div id="target" aria-hidden="true"><a href="/" style="display:none">Link</a></div>
<input id="target" disabled aria-hidden="true" />
<fieldset id="target" disabled aria-hidden="true"><input /></fieldset>