# An element with the attribute “tabindex” must not appear as a descendant of an element with the attribute “role=button”.

> Canonical HTML version: https://rocketvalidator.com/html-validation/an-element-with-the-attribute-tabindex-must-not-appear-as-a-descendant-of-an-element-with-the-attribute-role-button
> Attribution: Rocket Validator (https://rocketvalidator.com)
> License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

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

1. **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 — no `role` or `tabindex` needed.

2. **Move `tabindex` to the `role="button"` container.** If you must use `role="button"` (for example, when a `<div>` needs to behave as a button due to design constraints), place `tabindex="0"` on the container itself and remove `tabindex` from all descendants.

3. **Remove `tabindex` from descendants.** If the inner element doesn't actually need to be independently focusable, simply remove the `tabindex` attribute 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"`

```html
<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"`

```html
<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

```html
<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

```html
<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

```html
<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.
