Skip to content

Latest commit

 

History

History
213 lines (162 loc) · 5.48 KB

File metadata and controls

213 lines (162 loc) · 5.48 KB

Tabs

tabs — Tab Container

<!-- 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>

Tabs Attributes

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

tab — Tab Button

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>

Tab Attributes

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

panel — Tab Panel

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>

Panel Attributes

Attribute Type Default Description
panel boolean attr required Marks the element as a tab panel

Tab Positions

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

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.


Keyboard Navigation

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

CSS Classes

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;
}

Built-in Style Behavior

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)

Accessibility

No.JS automatically adds:

  • role="tablist" on the generated tab list wrapper
  • role="tab" on each tab, with aria-selected and aria-controls pointing to its panel
  • role="tabpanel" on each panel, with aria-labelledby pointing to its tab
  • tabindex="0" on the active tab, tabindex="-1" on inactive tabs (roving tabindex)
  • tabindex="0" on each panel for keyboard access
  • aria-hidden="true" and inert on inactive panels (prevents focus and screen reader access)
  • aria-disabled="true" on disabled tabs

Next: Tree →