Tabs
Tabs render an equal-width row of options as a Win95 property-sheet tab strip. Exactly one tab is active at a time; clicking a tab calls back with its id so the parent can swap the content shown beneath it.
Overview
When to use
- To switch between a small, fixed set of mutually-exclusive views or datasets in place (e.g. picking which day's training is active).
- When every option should stay visible and reachable in one click — unlike a Select, which hides the other options until opened.
When not to use
- For a long or dynamic list of options — use a Select or ListBox instead; equal-width tabs get cramped past a handful of items.
- For a single on/off toggle — use a Chip or Checkbox.
States
Inactive — recessed, color-bg-surface-muted fill, sits behind the active tab's baseline.
Active — pops forward: taller, color-accent-primary fill matching the content surface below it, and a higher z-index so it visually overlaps its neighbors.
Hover — brightness(0.95) darkens the tab surface. Applies to both active and inactive tabs.
Many items
Each tab shares the strip equally (flex: 1 1 0%), so labels should stay short — long labels in a wide tab set will wrap or truncate awkwardly.
Controls
Interactive controls are available in Storybook. Open Storybook
Props
Token mapping
| State | Property | Token |
|---|---|---|
| inactive | background | --color-bg-surface-muted |
| active | background | --color-accent-primary |
| all | text color | --color-text-on-accent |
| all | raised bevel | --sunken-outer-light, --sunken-outer-dark, --sunken-inner-light, --sunken-inner-dark |
| inactive | shared baseline | --panel-border-outer |
| active | shared baseline | --color-accent-primary |
Do's and don'ts
Do
- Pass a stable, unique
idper item — it's both the React key and the value handed back toonChange. - Provide an
aria-labelonTabsdescribing what the strip switches between (e.g. "Switch training day").
Don't
- Nest interactive elements inside a tab's
label— tabs render as plain<button>text, not arbitrary children. - Use Tabs to switch between more than a handful of options — the equal-width layout doesn't scale to long lists.
Changelog
| Date | Change |
|---|---|
| 2026-08-12 | Promoted from Gym's local TrainingSwitcher into a general-purpose base-components component — generalized trainings/name props to items/label, ported the plain-CSS bevel to Tailwind utility classes matching this package's convention, added stories, docs, and index |