Keep labels to one or two words for fast scanning.
Tabs
Segmented navigation for switching between related content panels within the same context.
- .source-tabs Tab bar container
- .source-tab Individual tab trigger
- [aria-selected="true"] Active tab state
- .source-tabs-indicator Animated underline indicator
- .source-tab-panel Linked content panel
States
Design intent
Tabs work when the user already knows roughly what to expect in each panel. If they need to explore without a clear destination, tabs create confusion. Use tabs for parallel views of the same object, not for sequential steps or top-level navigation between unrelated sections.
When to use
Use tabs when users only need one panel at a time.
Use for alternate views of the same entity or context.
Avoid labels that wrap, truncate, or read like instructions.
Do not use tabs for ordered step-by-step flows.
Do not replace page navigation with tabs across unrelated sections.
Palette
Default palette is neutral. Use palette="accent" (or any semantic palette) when you want tinted active and hover states.
Tab labels
Keep labels short, one or two words. Describe the content type, not the action. "Code" not "View code". "Accessibility" not "Check accessibility". Avoid tabs for navigation between unrelated pages; use a nav component instead.
Designer checklist
- Tab labels are one or two words. No sentence-length labels.
- The number of tabs is five or fewer. More tabs degrade to a navigation problem.
- Tab panels are not used for sequential steps that must be completed in order.
- The active tab is identifiable without relying only on color.
Tabs props
Tab props
TabPanel props
Usage
Import the component styles once at your app root, then use Tabs, Tab, and TabPanel anywhere in your tree. Pass defaultTab for uncontrolled use or activeTab + onTabChange for controlled.
import '@pierredelattre/source-tokens';
import '@pierredelattre/source-react/styles';
import { Tabs, Tab, TabPanel } from '@pierredelattre/source-react';
function Demo() {
return (
<Tabs defaultTab="overview">
<Tab id="overview">Overview</Tab>
<Tab id="code">Code</Tab>
<Tab id="settings" disabled>Settings</Tab>
<TabPanel id="overview">
<p>Overview content</p>
</TabPanel>
<TabPanel id="code">
<p>Code content</p>
</TabPanel>
<TabPanel id="settings">
<p>Settings content</p>
</TabPanel>
</Tabs>
);
}
Accent palette
<Tabs defaultTab="overview" palette="accent">
<Tab id="overview">Overview</Tab>
<Tab id="code">Code</Tab>
<TabPanel id="overview">...</TabPanel>
<TabPanel id="code">...</TabPanel>
</Tabs>
Controlled
const [active, setActive] = useState('overview');
<Tabs activeTab={active} onTabChange={setActive}>
<Tab id="overview">Overview</Tab>
<Tab id="code">Code</Tab>
<TabPanel id="overview">...</TabPanel>
<TabPanel id="code">...</TabPanel>
</Tabs>
Component tokens
All tokens are defined on .source-tabs and reference global semantic tokens by default. Override on a parent selector to restyle in a specific context without touching globals.
.sidebar .source-tabs {
--source-tab-indicator-color: var(--source-color-info);
--source-tab-bg-active: color-mix(in srgb, var(--source-color-info) 8%, transparent);
}
ARIA attributes
true on the active tab, false on all others. Updated by JS on activation.