Tabs organize content into separate views within the same context. Users can switch between tabs without navigating away from the current page.
Usage
<div class="of-tabs">
<button class="of-tab is-active">Active</button>
<button class="of-tab">Paused</button>
<button class="of-tab">Closed</button>
</div>
<div class="of-tab-panels">
<div class="of-tab-panel is-active">Panel 1 content</div>
<div class="of-tab-panel">Panel 2 content</div>
<div class="of-tab-panel">Panel 3 content</div>
</div>
Default style
Active tab panel content appears here.
Pill variant
Use pills for a softer, more compact tab appearance.
With badges
Add badges to tabs for counts or status indicators.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'default' | 'pills' | 'default' | Visual style |
value | string | — | Currently active tab value |
defaultValue | string | — | Initial active tab (uncontrolled) |
onChange | (value: string) => void | — | Called when tab selection changes |
Accessibility
- Use
role="tablist"on the container androle="tab"on each button - The active tab should have
aria-selected="true" - Inactive tabs should have
aria-selected="false" - Connect tab panels with
aria-labelledbyreferencing the tab ID
<div class="of-tabs" role="tablist">
<button class="of-tab is-active" role="tab" aria-selected="true" id="tab-1">Overview</button>
<button class="of-tab" role="tab" aria-selected="false" id="tab-2">Details</button>
</div>
<div role="tabpanel" aria-labelledby="tab-1">...</div>
Guidelines
Tab labels. Keep labels short — one or two words. Use sentence case.
Tab count. Aim for 2–5 tabs. More than that suggests the content needs reorganization.
Consistent width. Tabs can be equal width or content-width. Don’t mix within the same tab bar.
State persistence. Remember the selected tab across page reloads when it improves the user experience.
Disabled tabs. Avoid disabled tabs — if a section isn’t available, don’t show the tab at all.
JavaScript behavior
// Simple tab switching
const tabs = document.querySelectorAll('.of-tab');
const panels = document.querySelectorAll('.of-tab-panel');
tabs.forEach((tab, index) => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('is-active'));
panels.forEach(p => p.classList.remove('is-active'));
tab.classList.add('is-active');
panels[index].classList.add('is-active');
});
});