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

PropTypeDefaultDescription
variant'default' | 'pills''default'Visual style
valuestringCurrently active tab value
defaultValuestringInitial active tab (uncontrolled)
onChange(value: string) => voidCalled when tab selection changes

Accessibility

  • Use role="tablist" on the container and role="tab" on each button
  • The active tab should have aria-selected="true"
  • Inactive tabs should have aria-selected="false"
  • Connect tab panels with aria-labelledby referencing 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');
  });
});