Tabs
A set of horizontally aligned panels switched by clickable triggers.
Usage
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@epilot/spark-ui/tabs'<Tabs defaultValue="account">
<TabsList aria-label="Settings">
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
</TabsList>
<TabsContent value="account">Account…</TabsContent>
<TabsContent value="password">Password…</TabsContent>
</Tabs>There is a single TabsTrigger: it adapts to the closest TabsList's variant
automatically (no per-variant trigger components).
Portal
The end-customer portal tab. Bold neutral label, a 4px accent bar under the
active tab only, and no sliding indicator. Inactive tabs keep the bar's space
empty. The strip does not scroll itself — wrap it in ScrollArea when the
labels can overflow.
Examples
With a counter
Icon + text
Compose an icon before the label. The trigger centers both.
Icon only
Pass an aria-label (there's no visible text) and narrow the tab with a
min-w override.
Text + subline
Stack a title over a lighter subline.
Fill
Pass fill on TabsList to stretch the strip to its container's full width
and divide it evenly among the triggers, instead of each one sizing to its own
label. Each trigger's min-w floor still applies, so a strip with too many
tabs to keep every one that wide hands over to the scroller, same as the
default case.
Invalid
Set aria-invalid on a TabsTrigger whose panel holds a field error: the label and its underline take the error ink.
Overflow
Mobile-first: when the tabs don't fit, the list scrolls horizontally on its own, no wrapper needed. The overflowing edge shows a frosted-glass chevron button that scrolls on click; selecting a partially-visible tab travels it fully into view, revealing a slice of the next one. Keyboard focus auto-scrolls too, and the underline bar slides with its tab. Scrollbars are hidden.
High contrast
Colors
The active ink and the underline bar follow the accent knob.
API Reference
Built on Base UI Tabs. See the Base UI docs for the full API. Spark additions:
| Prop | Type | Default |
|---|---|---|
TabsList.variant | "underline" | "portal" | "underline" |
TabsList.fill | boolean | false |
underlineis the Spark tab: bold label, a compact hover box around the label (not the full-height trigger), and a per-tab bottom rail (neutral) with a 4px accent bar that worms to the active tab (stretches across, then contracts).portalis the end-customer portal tab: the same bold label in neutral ink, a 16px gap, and a 4px accent bar (base-accent-solid) drawn under the active tab only. The bar does not move or fade. The strip does not grow a rail, chevrons, or its own scroller.fillstretchesTabsListto100%width and divides it evenly among the triggers (flex-grow/flex-shrink/flex-basis-0), instead of each one sizing to its own label. The floor each trigger won't shrink below is unaffected — a strip too narrow to keep every trigger at that width still hands over to the scroller.aria-invalidonTabsTriggerpaints its label in the error ink and its rail (and the bar, while active) in the invalid-field border colour.- Hover is neutral (soft fill, ink stays neutral until selected, which is Base UI behavior).
- Tabs scroll horizontally when they overflow their container (mobile-first, built-in).
variant="pill"/"default"are deprecated:pill(a segmented control) isn't part of Spark's tab design; it still renders for back-compat but is undocumented.- Tabs are not
data-style-aware (a tab strip has no solid/soft/surface axis); they theme via the accent, neutral, radius, scaling, anddata-high-contrastknobs. TabsTriggerUnderline/TabsTriggerPillare deprecated aliases ofTabsTrigger(kept for back-compat);variant="default"is a deprecated alias ofunderline.