# An element with “role=tab” must be contained in, or owned by, an element with “role=tablist”.

> Canonical HTML version: https://rocketvalidator.com/html-validation/an-element-with-role-tab-must-be-contained-in-or-owned-by-an-element-with-role-tablist
> Attribution: Rocket Validator (https://rocketvalidator.com)
> License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

The WAI-ARIA specification defines a strict ownership model for tab-related roles. An element with `role="tab"` controls the visibility of an associated `role="tabpanel"` element, and tabs are expected to be grouped within a `tablist`. This relationship is how assistive technologies like screen readers understand and communicate the tab interface pattern to users — for example, announcing "tab 2 of 4" when focus moves between tabs.

When a `tab` is not contained in or owned by a `tablist`, screen readers cannot determine how many tabs exist in the group, which tab is currently selected, or how to navigate between them. This fundamentally breaks the accessibility of the tab interface, making it confusing or unusable for people who rely on assistive technologies.

There are two ways to establish the required relationship:

1. **Direct containment**: Place the `role="tab"` elements as direct children of the `role="tablist"` element. This is the most common and straightforward approach.
2. **Using `aria-owns`**: If the DOM structure prevents direct nesting, add `aria-owns` to the `tablist` element with a space-separated list of `id` values referencing each tab. This tells assistive technologies that the `tablist` owns those tabs even though they aren't direct children in the DOM.

## Examples

### Incorrect: tab outside of a tablist

In this example, the `role="tab"` buttons are siblings of the `tablist` rather than children of it, which triggers the validation error.

```html
<div class="tabs">
  <div role="tablist" aria-label="Sample Tabs"></div>
  <button role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1">
    First Tab
  </button>
  <button role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2">
    Second Tab
  </button>
</div>
```

### Correct: tabs as direct children of tablist

The simplest fix is to place the `role="tab"` elements directly inside the `role="tablist"` element.

```html
<div class="tabs">
  <div role="tablist" aria-label="Sample Tabs">
    <button role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1" tabindex="0">
      First Tab
    </button>
    <button role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">
      Second Tab
    </button>
  </div>
  <div id="panel-1" role="tabpanel" tabindex="0" aria-labelledby="tab-1">
    <p>Content for the first panel</p>
  </div>
  <div id="panel-2" role="tabpanel" tabindex="0" aria-labelledby="tab-2" hidden>
    <p>Content for the second panel</p>
  </div>
</div>
```

### Correct: using aria-owns when DOM nesting isn't possible

If your layout or framework makes it difficult to nest the tabs directly inside the `tablist`, you can use `aria-owns` to establish the relationship programmatically.

```html
<div class="tabs">
  <div role="tablist" aria-label="Sample Tabs" aria-owns="tab-1 tab-2"></div>
  <div class="tab-wrapper">
    <button role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1" tabindex="0">
      First Tab
    </button>
    <button role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">
      Second Tab
    </button>
  </div>
  <div id="panel-1" role="tabpanel" tabindex="0" aria-labelledby="tab-1">
    <p>Content for the first panel</p>
  </div>
  <div id="panel-2" role="tabpanel" tabindex="0" aria-labelledby="tab-2" hidden>
    <p>Content for the second panel</p>
  </div>
</div>
```

### Additional notes

- Each `role="tab"` element should use `aria-selected` to indicate which tab is active (`"true"`) and which are not (`"false"`).
- Use `aria-controls` on each tab to reference the `id` of its associated `tabpanel`.
- Use `aria-labelledby` on each `tabpanel` to point back to its controlling tab.
- Set `tabindex="0"` on the active tab and `tabindex="-1"` on inactive tabs to support keyboard navigation with arrow keys within the `tablist`.
- Always include an `aria-label` or `aria-labelledby` on the `tablist` to give it an accessible name.
