# Bad value “tabpanel” for attribute “role” on element “ul”.

> Canonical HTML version: https://rocketvalidator.com/html-validation/bad-value-tabpanel-for-attribute-role-on-element-ul
> Attribution: Rocket Validator (https://rocketvalidator.com)
> License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

The W3C validator raises this error because ARIA roles must be compatible with the element they are applied to. A `<ul>` element has an implicit ARIA role of `list`, and overriding it with `tabpanel` creates a conflict. The `tabpanel` role signals to assistive technologies that the element is a panel of content activated by a corresponding tab. When this role is placed on a `<ul>`, screen readers lose the semantic meaning of the list (item count, list navigation, etc.) while also misrepresenting the element's function in the tab interface.

This matters for several reasons:

- **Accessibility**: Screen reader users rely on correct roles to navigate and understand page structure. A `<ul>` marked as `tabpanel` confuses both its list semantics and its role in the tab interface.
- **Standards compliance**: The ARIA in HTML specification defines which roles are allowed on which elements. The `tabpanel` role is not permitted on `<ul>`.
- **Browser behavior**: Browsers may handle conflicting roles inconsistently, leading to unpredictable behavior across assistive technologies.

The fix is straightforward: wrap the `<ul>` inside a proper container element (like a `<div>` or `<section>`) and apply the `tabpanel` role to that container instead.

## Examples

### Incorrect: `tabpanel` role on a `<ul>`

This triggers the validation error because `tabpanel` is not a valid role for `<ul>`:

```html
<div role="tablist" aria-label="Recipe categories">
  <button role="tab" aria-controls="panel-1" aria-selected="true" id="tab-1">Appetizers</button>
  <button role="tab" aria-controls="panel-2" aria-selected="false" id="tab-2">Desserts</button>
</div>

<ul role="tabpanel" id="panel-1" aria-labelledby="tab-1">
  <li>Bruschetta</li>
  <li>Spring rolls</li>
</ul>

<ul role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
  <li>Tiramisu</li>
  <li>Cheesecake</li>
</ul>
```

### Correct: `tabpanel` role on a container wrapping the `<ul>`

Move the `tabpanel` role to a `<div>` and nest the `<ul>` inside it. This preserves both the tab panel semantics and the list semantics:

```html
<div role="tablist" aria-label="Recipe categories">
  <button role="tab" aria-controls="panel-1" aria-selected="true" id="tab-1">Appetizers</button>
  <button role="tab" aria-controls="panel-2" aria-selected="false" id="tab-2">Desserts</button>
</div>

<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
  <ul>
    <li>Bruschetta</li>
    <li>Spring rolls</li>
  </ul>
</div>

<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
  <ul>
    <li>Tiramisu</li>
    <li>Cheesecake</li>
  </ul>
</div>
```

### Correct: Using `<section>` as the tab panel

A `<section>` element also works well as a tab panel container, especially when the panel content is more complex:

```html
<div role="tablist" aria-label="Project info">
  <button role="tab" aria-controls="tasks-panel" aria-selected="true" id="tasks-tab">Tasks</button>
  <button role="tab" aria-controls="notes-panel" aria-selected="false" id="notes-tab">Notes</button>
</div>

<section role="tabpanel" id="tasks-panel" aria-labelledby="tasks-tab">
  <h2>Current tasks</h2>
  <ul>
    <li>Review pull requests</li>
    <li>Update documentation</li>
  </ul>
</section>

<section role="tabpanel" id="notes-panel" aria-labelledby="notes-tab" hidden>
  <h2>Meeting notes</h2>
  <p>Discussed project timeline and milestones.</p>
</section>
```

In a properly structured tabbed interface:

- The `tablist` role goes on the container that holds the tab buttons.
- Each tab trigger gets `role="tab"` with `aria-controls` pointing to its panel's `id`.
- Each content panel gets `role="tabpanel"` on a generic container like `<div>` or `<section>`, with `aria-labelledby` referencing the corresponding tab's `id`.
- List elements like `<ul>` and `<ol>` should remain inside the panel as regular content, retaining their native list semantics.
