axe-core
axe-core copied to clipboard
Nested-interactive seemingly not flagging controls nested within links
Product
axe-core
Product Version
4.7.2
Latest Version
- [X] I have tested the issue with the latest version of the product
Issue Description
Expectation
Based on the wording of nested-interactive, I expect controls (e.g. buttons, links, whether with native HTML or explicit ARIA roles) nested within links to be a violation of this rule: "Checks all interactive controls and ensures they do not contain focusable child elements."
Actual
Buttons and links nested within links (both native HTML anchor elements and custom elements with the ARIA link role) do not seem to be considered violations of this rule. I've added "violation" or "fine" in parentheses in the example below based on the results I was getting from devTools. I've confirmed in most cases these do create focusable child elements in the accessibility tree, at least with the accessibility tree shown in Chrome dev tools.
How to Reproduce
<!--
Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0
-->
<h1>nested buttons - mostly violations</h1>
<button>placeholder<button>button inside button (fine)</button></button>
<button><div role="button" tabindex="0">button role inside button (violation)</div></button>
<div role="button" tabindex="0"><button>button inside button role (violation)</button></div>
<div role="button" tabindex="0"><div role="button" tabindex="0">button role inside button role (violation)</div></div>
<h1>links nested within buttons - all violations</h1>
<button><a href="#">link inside button (violation)</a></button>
<button><div role="link" tabindex="0">link role inside button (violation)</div></button>
<div role="button" tabindex="0"><a href="#">link inside button role (violation)</a></div>
<div role="button" tabindex="0"><div role="link" tabindex="0">link role inside button role (violation)</div></div>
<h1>buttons nested within links - no violations</h1>
<a href="#"><button>button inside link (fine)</button></a>
<a href="#"><div role="button" tabindex="0">button role inside link (fine)</div></a>
<div role="link" tabindex="0"><button>button inside link role (fine)</button></div>
<div role="link" tabindex="0"><div role="button" tabindex="0">button role inside link role (fine)</div></div>
<h1>links nested within links - no violations</h1>
<a href="#">placeholder<a href="#">link inside link (fine)</a></a>
<a href="#"><div role="link" tabindex="0">link role inside link (fine)</div></a>
<div role="link" tabindex="0"><a href="#">link inside link role (fine)</a></div>
<div role="link" tabindex="0"><div role="link" tabindex="0">link role inside link role (fine)</div></div>
Additional context
My devTools extension is running 4.7.2 and I don't see a way to update to v4.8+, but the update info for v4.8+ doesn't seem to mention changes that would be relevant. Apologies if this causes any issues with this report!
Thanks for the issue. I can see where the confusion is.
The rule has a qualifier (that we seem to have not documented) that it only applies to roles that have "Children Presentational." This is the reason why nested controls are bad for screen readers as any children of the element are automatically presentational so do not announce. The button role is one such role, but the link role is not. This is why anything nested within a link does not return as a violation.
I'll make a note to update the docs to be more clear about this. I think when we first wrote the rule we flagged links, but I think we modified it later on and forgot to modify the description as well.
As for this case:
<button>placeholder<button>button inside button (fine)</button></button>
This is incorrect markup as buttons are not allowed to be inside buttons, so most browsers will correct this for you and make it two separate buttons when rendered, which is why it passes.
<button>placeholder</button>
<button>button inside button (fine)</button>
On the above, buttons will not correct this if you nest those buttons using something like React. We should also make it clearer that this is about nesting controls, not just anything focusable like the docs suggests with phrases like this:
Interactive control elements must not have focusable descendants.
The nested links actually result in VoiceOver issues. For example, users can not open the link in the following example by "VO + Space":
<!--
Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0
-->
<div>Use "VO + Space" to check if the link can be opened</div>
<br />
<div role="link">
<a href="https://github.com/">
github link
</a>
</div>
If nested-interactive is not designed for check link, could we have some other check for the "nested-links"?