Tabs

Semantic ARIA-first tabs styling using role=tablist, role=tab, and role=tabpanel.

Overview

Tabs are styled semantically from ARIA roles rather than a required custom component wrapper.

Interactive ARIA tabs

Try the arrow, Home, and End keys while a tab is focused.

Update your public name, avatar, and biography.

Required Roles

  • role="tablist"
  • role="tab"
  • role="tabpanel"

Basic Example

<div role="tablist" aria-label="Demo tabs">
<button role="tab" aria-selected="true" aria-controls="panel-a" id="tab-a">Overview</button>
<button role="tab" aria-selected="false" aria-controls="panel-b" id="tab-b" tabindex="-1">API</button>
</div>
<div role="tabpanel" id="panel-a" aria-labelledby="tab-a">Overview content</div>
<div role="tabpanel" id="panel-b" aria-labelledby="tab-b" hidden>API content</div>

States

  • active: aria-selected="true"
  • disabled: aria-disabled="true" or disabled
  • hidden panels: hidden

Optional Selector Aliases

  • [data-tablist], .tablist
  • [data-tab], .tab
  • [data-tabpanel], .tabpanel

Tokens

  • --tabs-gap
  • --tabs-padding-inline
  • --tabs-padding-block
  • --tabs-border-color
  • --tabs-background
  • --tabs-tab-color
  • --tabs-tab-hover-background
  • --tabs-tab-hover-color
  • --tabs-tab-active-background
  • --tabs-tab-active-color
  • --tabs-tab-active-border
  • --tabs-panel-background
  • --tabs-panel-border
  • --tabs-panel-radius
  • --tabs-panel-padding
  • --tabs-indicator-width

Notes

  • CSS styles the ARIA states; JavaScript should manage selection, focus movement, and panel visibility.
  • Vertical tablists are supported with aria-orientation="vertical".

Behavior checklist

  • Exactly one tab should have aria-selected="true" and tabindex="0".
  • Inactive tabs should use aria-selected="false" and tabindex="-1".
  • Each tab’s aria-controls must match its panel id.
  • Each panel’s aria-labelledby must match its tab id.
  • Left/Right Arrow, Home, and End should move focus for a horizontal tablist.
  • Toggle the panel’s hidden attribute when selection changes.

The live example above includes this behavior. CSS alone intentionally does not change ARIA state.

No-JavaScript option

When the content must remain interactive without JavaScript, use the native disclosure-driven pattern instead of assigning ARIA tab roles without their required behavior. A shared name keeps one panel open at a time in supporting browsers; elsewhere it remains a useful group of ordinary disclosures.

Native disclosure tabs

Disable JavaScript and the summaries still open their associated content.

Overview

Native details provide activation, focus behavior, and open state.

API

Set —tab-count on the host and —n on each item.

Fallback

The same markup falls back to stacked disclosure widgets.

Use formal ARIA tabs when arrow-key tab semantics are required. Use this native pattern when resilient no-script interaction is the priority.