<!-- Basic tabs (first tab active by default) -->
<div tabs>
<button tab>Profile</button>
<button tab>Settings</button>
<button tab>Billing</button>
<div panel>Profile content...</div>
<div panel>Settings content...</div>
<div panel>Billing content...</div>
</div>
<!-- Activate a specific tab (0-based index) -->
<div tabs="2">
<button tab>Tab A</button>
<button tab>Tab B</button>
<button tab>Tab C</button>
<div panel>Panel A</div>
<div panel>Panel B</div>
<div panel>Panel C (active)</div>
</div>| Attribute | Type | Default | Description |
|---|---|---|---|
tabs |
number (0-based index) | 0 |
Initial active tab index |
tab-position |
"top" | "bottom" | "left" | "right" |
"top" |
Placement of the tab list relative to panels |
Marks a direct child of [tabs] as a tab trigger. Each [tab] is paired with a [panel] by order.
<div tabs>
<button tab>Enabled</button>
<button tab tab-disabled="true">Disabled</button>
<button tab>Also enabled</button>
<div panel>First panel</div>
<div panel>Second panel (unreachable)</div>
<div panel>Third panel</div>
</div>| Attribute | Type | Default | Description |
|---|---|---|---|
tab |
boolean attr | required | Marks the element as a tab trigger |
tab-disabled |
expression | false |
When truthy, the tab cannot be activated |
Marks a direct child of [tabs] as a panel. Panels are paired with tabs by order: the first [tab] controls the first [panel], the second controls the second, and so on.
<div tabs>
<button tab>Overview</button>
<button tab>Details</button>
<div panel>
<h2>Overview</h2>
<p bind="summary"></p>
</div>
<div panel>
<h2>Details</h2>
<ul>
<li each="item in items" bind="item.name"></li>
</ul>
</div>
</div>| Attribute | Type | Default | Description |
|---|---|---|---|
panel |
boolean attr | required | Marks the element as a tab panel |
Use tab-position to control where the tab list appears relative to the panels.
<!-- Tabs on top (default) -->
<div tabs tab-position="top">
<button tab>One</button>
<button tab>Two</button>
<div panel>Panel One</div>
<div panel>Panel Two</div>
</div>
<!-- Tabs on the bottom -->
<div tabs tab-position="bottom">
<button tab>One</button>
<button tab>Two</button>
<div panel>Panel One</div>
<div panel>Panel Two</div>
</div>
<!-- Tabs on the left (vertical) -->
<div tabs tab-position="left">
<button tab>One</button>
<button tab>Two</button>
<div panel>Panel One</div>
<div panel>Panel Two</div>
</div>
<!-- Tabs on the right (vertical) -->
<div tabs tab-position="right">
<button tab>One</button>
<button tab>Two</button>
<div panel>Panel One</div>
<div panel>Panel Two</div>
</div>Disabled tabs are skipped by keyboard navigation and cannot be activated by click.
<div state="{ locked: true }">
<div tabs>
<button tab>Open</button>
<button tab tab-disabled="locked">Locked</button>
<button tab>Also open</button>
<div panel>Accessible panel</div>
<div panel>This panel cannot be reached</div>
<div panel>Also accessible</div>
</div>
</div>If the initial tab (index 0 or the value of tabs) is disabled, the next enabled tab is activated automatically.
| Key | Action |
|---|---|
ArrowRight / ArrowDown |
Move to next enabled tab (wraps around) |
ArrowLeft / ArrowUp |
Move to previous enabled tab (wraps around) |
Home |
Move to first enabled tab |
End |
Move to last enabled tab |
Tab |
Move focus from the tab list into the active panel |
No.JS automatically injects these classes:
| Class | When applied |
|---|---|
.nojs-tabs |
On the [tabs] container |
.nojs-tablist |
On the generated role="tablist" wrapper |
.nojs-tab |
On each [tab] element |
.nojs-panel |
On each [panel] element |
The container also receives a data-position attribute matching the tab-position value, which you can use for custom styling:
/* Custom vertical tab styling */
.nojs-tabs[data-position="left"] .nojs-tablist {
border-right: 2px solid #ccc;
}| Selector | Style |
|---|---|
.nojs-tab[aria-disabled="true"] |
pointer-events: none; opacity: 0.5 |
.nojs-panel[aria-hidden="true"] |
display: none |
.nojs-tabs[data-position="left"], [data-position="right"] |
Horizontal layout (flex-direction: row) |
.nojs-tabs[data-position="bottom"] |
Reversed column layout |
.nojs-tabs[data-position="left/right"] .nojs-tablist |
Vertical tab list (flex-direction: column) |
No.JS automatically adds:
role="tablist"on the generated tab list wrapperrole="tab"on each tab, witharia-selectedandaria-controlspointing to its panelrole="tabpanel"on each panel, witharia-labelledbypointing to its tabtabindex="0"on the active tab,tabindex="-1"on inactive tabs (roving tabindex)tabindex="0"on each panel for keyboard accessaria-hidden="true"andinerton inactive panels (prevents focus and screen reader access)aria-disabled="true"on disabled tabs
Next: Tree →