About This HTML Issue
When an element has role="button", assistive technologies treat it as a single interactive control — just like a native <button>. Users expect to tab to it once, and then activate it with Enter or Space. If a focusable descendant (an element with tabindex) exists inside that button, it creates a second tab stop within what should be a single control. This breaks the expected interaction model and confuses both keyboard users and screen readers.
The WAI-ARIA specification explicitly states that certain roles, including button, must not contain interactive or focusable descendants. This is because a button is an atomic widget — it represents one action and should receive focus as a single unit. When a screen reader encounters a role="button" element, it announces it as a button and expects the user to interact with it directly. A nested focusable element disrupts this by creating an ambiguous focus target: should the user interact with the outer button or the inner focusable element?
This issue commonly arises when developers wrap inline elements like <span> or <a> with tabindex inside a <div role="button">, often to style parts of the button differently or to add click handlers. The correct approach is to ensure only the outermost button-like element is focusable.
How to fix it
-
Use a native
<button>element. This is always the best solution. Native buttons handle focus, keyboard interaction (Enter and Space key activation), and accessibility announcements automatically — noroleortabindexneeded. -
Move
tabindexto therole="button"container. If you must userole="button"(for example, when a<div>needs to behave as a button due to design constraints), placetabindex="0"on the container itself and removetabindexfrom all descendants. -
Remove
tabindexfrom descendants. If the inner element doesn’t actually need to be independently focusable, simply remove thetabindexattribute from it.
When using role="button" on a non-interactive element, remember you also need to implement keyboard event handlers for Enter and Space to fully replicate native button behavior.
Examples
Incorrect: focusable descendant inside role="button"
<div role="button">
<span tabindex="0">Click me</span>
</div>
The <span> with tabindex="0" creates a focusable element inside the role="button" container, which violates the ARIA authoring rules.
Incorrect: anchor element inside role="button"
<div role="button" tabindex="0">
<a href="/action" tabindex="0">Perform action</a>
</div>
Even though the container itself is focusable, the nested <a> with tabindex is also focusable, creating two tab stops for what should be a single control.
Correct: use a native <button> element
<button>Click me</button>
A native <button> handles focus, keyboard events, and accessibility semantics out of the box with no additional attributes.
Correct: move tabindex to the role="button" container
<div role="button" tabindex="0">
<span>Click me</span>
</div>
The tabindex="0" is on the role="button" element itself, and the inner <span> is not independently focusable.
Correct: native button with styled inner content
<button>
<span class="icon">★</span>
<span class="label">Favorite</span>
</button>
You can still use inner elements for styling purposes inside a <button> — just don’t add tabindex to them. The button manages focus as a single unit, and screen readers announce the combined text content.
Find issues like this automatically
Rocket Validator scans thousands of pages in seconds, detecting HTML issues across your entire site.