Skip to main content

Tabs

Switch between mutually exclusive content panels via tabbed navigation.

Selectors

[data-tabs]
Container for the tab group. Activates cider.js behavior.
[data-tab-list]
Optional wrapper for tab buttons. Falls back to the first button's parent element. Receives role="tablist".
[data-tab="id"]
Tab button. The value links to a matching [data-tab-panel].
[data-tab-panel="id"]
Content panel toggled by the active tab. Hidden when inactive.
[data-tab-indicator]
Optional sliding indicator positioned by JS (used by Segmented Control).
[data-active]
Marks the initially active tab and panel.

Default

General settings content.

Notification preferences content.

Security settings content.

HTML
<div data-tabs>
  <div data-tab-list class="flex gap-1 border-b border-border-solid pb-px">
    <button data-tab="t1" data-active class="px-3 py-1.5 text-sm font-medium border-b-2 border-primary text-primary -mb-px">General</button>
    <button data-tab="t2" class="px-3 py-1.5 text-sm text-tertiary-foreground hover:text-foreground border-b-2 border-transparent -mb-px">Notifications</button>
    <button data-tab="t3" class="px-3 py-1.5 text-sm text-tertiary-foreground hover:text-foreground border-b-2 border-transparent -mb-px">Security</button>
  </div>
  <div data-tab-panel="t1" data-active>
    <p class="py-4 text-sm text-tertiary-foreground">General settings content.</p>
  </div>
  <div data-tab-panel="t2">
    <p class="py-4 text-sm text-tertiary-foreground">Notification preferences content.</p>
  </div>
  <div data-tab-panel="t3">
    <p class="py-4 text-sm text-tertiary-foreground">Security settings content.</p>
  </div>
</div>

Disabled Tab

This tab is active. The "Locked" tab cannot be selected.

This tab is available.

This panel is unreachable.

HTML
<div data-tabs>
  <div data-tab-list class="flex gap-1 border-b border-border-solid pb-px">
    <button data-tab="d1" data-active class="px-3 py-1.5 text-sm font-medium border-b-2 border-primary text-primary -mb-px">Active</button>
    <button data-tab="d2" class="px-3 py-1.5 text-sm text-tertiary-foreground hover:text-foreground border-b-2 border-transparent -mb-px">Available</button>
    <button data-tab="d3" disabled class="px-3 py-1.5 text-sm text-tertiary-foreground border-b-2 border-transparent -mb-px opacity-40 cursor-not-allowed">Locked</button>
  </div>
  <div data-tab-panel="d1" data-active>
    <p class="py-4 text-sm text-tertiary-foreground">This tab is active. The "Locked" tab cannot be selected.</p>
  </div>
  <div data-tab-panel="d2">
    <p class="py-4 text-sm text-tertiary-foreground">This tab is available.</p>
  </div>
  <div data-tab-panel="d3">
    <p class="py-4 text-sm text-tertiary-foreground">This panel is unreachable.</p>
  </div>
</div>

Vertical

Profile settings. Arrow Up/Down navigate tabs in vertical orientation.

Account settings content.

Billing information content.

HTML
<div data-tabs class="flex gap-6">
  <div data-tab-list data-orientation="vertical" class="flex flex-col gap-1 border-r border-border-solid pr-4 min-w-[120px]">
    <button data-tab="v1" data-active class="px-3 py-1.5 text-sm font-medium text-left text-primary bg-primary/10 rounded-[var(--radius-sm)]">Profile</button>
    <button data-tab="v2" class="px-3 py-1.5 text-sm text-left text-tertiary-foreground hover:text-foreground hover:bg-secondary/50 rounded-[var(--radius-sm)]">Account</button>
    <button data-tab="v3" class="px-3 py-1.5 text-sm text-left text-tertiary-foreground hover:text-foreground hover:bg-secondary/50 rounded-[var(--radius-sm)]">Billing</button>
  </div>
  <div data-tab-panel="v1" data-active class="flex-1">
    <p class="text-sm text-tertiary-foreground">Profile settings. Arrow Up/Down navigate tabs in vertical orientation.</p>
  </div>
  <div data-tab-panel="v2" class="flex-1">
    <p class="text-sm text-tertiary-foreground">Account settings content.</p>
  </div>
  <div data-tab-panel="v3" class="flex-1">
    <p class="text-sm text-tertiary-foreground">Billing information content.</p>
  </div>
</div>

Usage

Tabs is a behavioral layer — it manages activation, ARIA attributes, and keyboard navigation. Visual styling is up to you (Tailwind utilities, custom CSS, or the Segmented Control component).

<!-- npm -->
<script src="node_modules/ciderui/js/cider.js"></script>

<!-- CDN -->
<script src="https://cdn.jsdelivr.net/gh/newlix/ciderui@main/js/cider.js"></script>

Keyboard

KeyAction
Arrow Right / Arrow LeftMove to next / previous tab (horizontal orientation)
Arrow Down / Arrow UpMove to next / previous tab (vertical orientation)
HomeMove to first tab
EndMove to last tab
Enter / SpaceActivate focused tab

Accessibility

  • Sets role="tablist", role="tab", and role="tabpanel" automatically.
  • Manages aria-selected, aria-controls, aria-labelledby, and tabindex roving focus.
  • Set data-orientation="vertical" on [data-tab-list] for vertical arrow key navigation.
  • Disabled tabs (disabled or aria-disabled="true") are skipped during keyboard navigation.

API

CiderUI.tabs.init()
Scans the document for [data-tabs] elements and initializes them. Called automatically on DOMContentLoaded and htmx:afterSettle.
CiderUI.tabs.destroy(tabGroupElement)
Tears down event listeners and ARIA attributes for a specific tab group.