Skip to main content
The Tabs component provides an accessible tabbed interface with automatic keyboard navigation. It uses the <ot-tabs> web component to manage tab state and panel visibility.

Basic Usage

Wrap tab buttons and panels in <ot-tabs>. Use role="tablist", role="tab", and role="tabpanel".
<ot-tabs>
  <div role="tablist">
    <button role="tab">Account</button>
    <button role="tab">Password</button>
    <button role="tab">Notifications</button>
  </div>
  <div role="tabpanel">
    <h3>Account Settings</h3>
    <p>Manage your account information here.</p>
  </div>
  <div role="tabpanel">
    <h3>Password Settings</h3>
    <p>Change your password here.</p>
  </div>
  <div role="tabpanel">
    <h3>Notification Settings</h3>
    <p>Configure your notification preferences.</p>
  </div>
</ot-tabs>

Features

Keyboard Navigation

Use Arrow Left/Right to navigate between tabs

ARIA Support

Automatic aria-controls, aria-labelledby, and aria-selected management

Auto IDs

Generates unique IDs for tabs and panels if not provided

Custom Events

Emits ot-tab-change event on tab change

Web Component API

Properties

activeIndex
number
Get or set the currently active tab index (0-based)

Events

ot-tab-change
CustomEvent
Fired when the active tab changes. Event detail includes:
  • index - The index of the newly active tab
  • tab - The tab element that was activated

JavaScript Usage

Setting Active Tab

const tabs = document.querySelector('ot-tabs');

// Set the second tab as active
tabs.activeIndex = 1;

// Get current active index
console.log(tabs.activeIndex); // 1

Listening to Tab Changes

const tabs = document.querySelector('ot-tabs');

tabs.addEventListener('ot-tab-change', (e) => {
  console.log('Tab changed to index:', e.detail.index);
  console.log('Tab element:', e.detail.tab);
});

HTML Structure

1

Wrap in ot-tabs

The <ot-tabs> element is required as the outer container
2

Add tablist

A single <div role="tablist"> containing all tab buttons
3

Add tab buttons

Each <button role="tab"> represents a clickable tab
4

Add panels

Each <div role="tabpanel"> contains the content for its corresponding tab

Initial Active Tab

To set a specific tab as initially active, add aria-selected="true" to the desired tab button:
<ot-tabs>
  <div role="tablist">
    <button role="tab">Tab 1</button>
    <button role="tab" aria-selected="true">Tab 2</button>
    <button role="tab">Tab 3</button>
  </div>
  <div role="tabpanel">Content 1</div>
  <div role="tabpanel">Content 2 (shown by default)</div>
  <div role="tabpanel">Content 3</div>
</ot-tabs>
If no tab has aria-selected="true", the first tab will be active by default.

Accessibility

The Tabs component automatically handles:
  • ARIA attributes: Sets aria-controls, aria-labelledby, and aria-selected
  • Keyboard navigation: Arrow keys move between tabs
  • Focus management: Active tab receives focus on keyboard navigation
  • Tab indexing: Only the active tab is focusable via Tab key
Ensure each tab has a corresponding panel. The component will log a warning if tabs or panels are missing.

Styling

The component uses CSS custom properties for theming:
[role="tablist"] {
  background-color: var(--muted);
  border-radius: var(--radius-medium);
  padding: var(--space-1);
  gap: var(--space-1);
}

[role="tab"] {
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-7);
  font-weight: var(--font-medium);
}

[role="tab"][aria-selected="true"] {
  background-color: var(--background);
  box-shadow: var(--shadow-small);
}