Tabs

Switches between panels of one view.

Examples

Conversation details

42 messages across 3 agents.

Many tabs in a narrow pane

Open documents

Unsaved changes

Styles

  • defaultdefault

  • line

  • Unsaved changes

    document

Sizes

  • defaultdefault

  • sm

States

  • rest

  • hover

  • focus-visible

  • disabled

Usage

When to use it
Use
One view has two to seven panels of the same object, read one at a time: thread, activity, settings.
Use
People keep several things open and switch between them: variant="document".
Not for
The choice changes how one set of data is shown, like a chart range. Use Segmented control.
Not for
One section shows or hides under a heading. Use Collapsible.
Not for
The person goes through the panels in order. Use Progress.

Rules

9 rules
Must
Give every TabsList aria-label naming the object: 'Conversation'. Because a screen reader announces an unnamed "tab list", so two tab sets on one page sound the same.
Must
Keep every tab about the same object at the same level, two to seven tabs. Because a tab bar reads as one object seen several ways, so Billing beside Activity hides a page people look for in navigation.
Must
Pass keepMounted to a TabsContent that holds typed or scrolled state. Because Base UI unmounts a hidden panel, so switching away and back drops the typed text and the scroll position.
Must
Select the next tab, or the previous when it was last, when a document tab closes. Because closing the selected tab leaves none selected, so the panel goes blank and focus drops to the page.
Must
Pass label with the full name on a pinned (icon-only) document tab. Because a pinned tab hides its text, so without label a screen reader reads the value: "run_8f2a, pinned".
Should
Label with a noun of one or two words under 20 characters and a TabsCount for how many.
Never
Mix pill, underline and per-tab icons; pick default or line and keep icons off. Because an underline on one tab and a chip on another both read as selected, so the person cannot tell which panel shows.
Should
Let a document tab label truncate at 220px and carry the full name in label.
Must
Render tabs only with at least two triggers. Because a single tab switches nothing, and a screen reader still announces a tab list of one.

Accessibility

8 checks
The list is a tablist named by aria-label; each trigger is a tab with aria-selected and aria-controls.
Tab focuses the selected tab; a second Tab goes to the panel.
Arrow keys move and select on default and line, skipping disabled and mirroring in RTL.
Document tabs use manual activation: arrows move focus only; Enter or Space selects.
Home and End jump to the first and last tab.
Delete or Backspace closes a closable document tab, named by aria-keyshortcuts. The chip's cross is aria-hidden, so the tablist holds only tabs; the selected tab's named Close button sits beside the list, for touch screen readers and voice control.
Alt+Left and Alt+Right reorder a document tab within its group when onReorder is set.
A dirty tab carries sr-only "unsaved changes".

Edge cases

11 edge cases
Long label at 320px
Tabs never wrap; the list scrolls with a fade. Document tabs cap at 220px.
25 tabs at 160px
One scroller with the end fade; the selected tab scrolls into view.
Right to left
Tabs mirror, arrows mirror, and the fade flips.
Zoom 200%
Tabs take min-height and grow; the list scrolls.
Kind on a document tab
Kept as data-kind but not drawn; the panel header writes the kind.
One-letter tab
Keeps 12px side padding.
Closing a dirty tab
onClose confirms first: Delete and Backspace close in one keystroke, and Backspace is easy to hit.
No triggers or no value
An empty row with its line renders and no panel shows.
Panel loading or failing
The panel owns Skeleton or Empty state; the tab switches at once.
Forced colors
The selected tab gets a 1px outline; the line paints Highlight.
Reduced motion
The line indicator jumps.

API

10 props
Tabs value, defaultValue, onValueChange, orientation
Base UI Tabs.Root default 'horizontal'
TabsList variant
'default' | 'line' | 'document' default 'default'
TabsList size
'default' | 'sm' default 'default'
TabsList onClose
(value) => void
TabsList onReorder
(values: string[]) => void
TabsList activateOnFocus
boolean default true; false for document
TabsList actions
ReactNode
TabsTrigger kind, pinned, dirty, label, icon
Kind, boolean, boolean, string, ReactNode
TabsContent value, keepMounted
Base UI Tabs.Panel
TabsCount
span

Tokens

19 tokens
--line-frame
oklch(var(--ink-c) / var(--line-frame-alpha))
--line-row
oklch(var(--ink-c) / 0.08)
--radius-soft
6px
--radius-inner
4px
--radius-frame
10px
--control
32px
--control-sm
28px
--space-6
24px
--text-2
oklch(var(--ink-c) / 0.80)
--text-1
var(--ink)
--surface-hover
oklch(var(--ink-c) / 0.05)
--surface-page
var(--paper)
--surface-selected
oklch(var(--ink-c) / 0.09)
--text-disabled
oklch(var(--ink-c) / 0.40)
--line-accent
2px
--dur-fast
120ms
--ease-step
cubic-bezier(0.2, 0, 0, 1)
--ink
oklch(var(--ink-c))
--tab-max
220px

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.