Tabs

Tabs

View in Storybook

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​

StatePropertyToken
inactivebackground--color-bg-surface-muted
activebackground--color-accent-primary
alltext color--color-text-on-accent
allraised bevel--sunken-outer-light, --sunken-outer-dark, --sunken-inner-light, --sunken-inner-dark
inactiveshared baseline--panel-border-outer
activeshared baseline--color-accent-primary

Do's and don'ts​

Do

  • Pass a stable, unique id per item — it's both the React key and the value handed back to onChange.
  • Provide an aria-label on Tabs describing 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​

DateChange
2026-08-12Promoted 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