Tabs
NewTabs split one view into a few sections the merchant moves between without leaving the page: a product's General, Variants and Inventory. A teal line marks the chosen tab and glides to the next one.
Playground
Change the props, then click along the tabs or use the arrow keys.
<Tabs defaultValue="general">
<TabsList aria-label="Product sections">
<TabsTrigger value="general">General</TabsTrigger>
<TabsTrigger value="media" count={5}>Media</TabsTrigger>
<TabsTrigger value="variants" count={3}>Variants</TabsTrigger>
<TabsTrigger value="shipping">Shipping</TabsTrigger>
</TabsList>
<TabsContent value="general">…</TabsContent>
</Tabs>Line: sections of a page
The default. Tabs sit on a hairline that runs the full width; the chosen one is ink at 600 with a 2px teal line under it, and one hover pill follows the pointer along the row. Each tab owns a TabsContent panel with the same value. Space the panel from the list yourself.
Includes VAT at 20%
Pill: a switch inside a card or a dialog
variant="pill" is the segmented control's track and gliding thumb, drawn from the same classes and tokens, so the two always match. Use it where a hairline would be lost: in a card header, in a dialog, beside a title. Here the tabs live in the card's header and the panels below it.
Top products
Last 30 days
- 1Arden ergonomic chair£4,980.00
- 2Oak standing desk£3,640.00
- 3Walnut monitor riser£1,188.00
- 4Cable tray£612.00
Steps: the stages of a task
variant="steps" numbers its tabs and gives each a status: a grey ring while not-started, half drawn in teal while in-progress, and filled teal with a check once completed. The path to the next step turns teal when a step is done, and the chosen step wears the soft halo of a focused field.
Disable the steps that cannot be reached yet: they stay readable, only quieter. Work through the steps below with Next, then go back to an earlier one.
Only you see this.
Sizes
sm 30px, md 36px (default) and lg 44px: the same heights as buttons and fields. Line labels are 13, 14 and 15px; pill labels follow the segmented control at 12, 13 and 14px. Reach for lg when the tabs head a whole page.
Icons and counts
icon leads the label; on a chosen line tab it turns teal, and activeIcon can swap in the duotone weight, as the sidebar does. count trails the label as a small badge that takes the teal tint on the chosen tab and bumps when the number changes.
Disabled
A disabled tab drops to 45%, cannot be chosen and is skipped by the arrow keys. Say nearby why it is off.
On narrow screens
A row wider than its container never wraps. It scrolls sideways, fades at whichever end has more to show, and scrolls the chosen tab into view, here Returns, the sixth of eight. Only the row scrolls, never the page.
An empty tab
A tab with nothing in it yet still opens. Show a count of 0 so the merchant knows before they look, and put a small Empty state in its panel.
No returns yet
When a customer sends something back, the return lands here.
Keyboard
Radix Tabs underneath: one Tab stop for the row, and the arrows move between tabs.
| Key | What it does |
|---|---|
| Tab | Moves focus to the chosen tab. Tab again moves into its panel. |
| Right and left arrows | Move to the next or previous tab and choose it, skipping disabled tabs, and wrap round at the ends. |
| Home, End | Move to the first or the last tab. |
| Enter, Space | Choose the focused tab when activationMode="manual" is set on Tabs, where the arrows only move focus. |
Motion
There is one teal line and one hover pill per row, and they move: the line glides to the chosen tab on the snappy spring, and the pill follows the pointer from tab to tab, so the eye follows a single shape instead of a run of flashes. The chosen label turns semibold without changing width. The new panel fades in, rising 4px.
Under reduced motion the line, the pill and the pill tabs' thumb jump straight to their place, and panels fade without rising.
In both themes
Line, pill and steps, light and dark side by side.
Guidelines
- Two to seven tabs, one or two words each, with the one most people need first.
- Moving between tabs never loses work: fields keep what was typed, and nothing is saved or discarded.
- Line tabs for the sections of a page, pill tabs inside a card or a dialog. Never one set of tabs inside another.
- Steps for a task that must be done in order. Disable the steps that cannot be reached yet.
- Tabs change the view in place. To go to another page, use a link: the sidebar or the breadcrumbs.
Tabs or something else
| The merchant needs | Use |
|---|---|
| Sections of one record, each with its own content | Tabs, line |
| A compact switch between panels in a card or a dialog | Tabs, pill |
| To work through the stages of a task in order | Tabs, steps |
| The same content seen another way: a period, a grid or a list | Segmented control |
| To go to another page | Links in the sidebar or the breadcrumbs |
Accessibility
- Built on Radix Tabs: a
tablistoftabbuttons, each wired to itstabpanel, witharia-selectedon the chosen one. - Name the list with
aria-labelwhen nothing nearby says what the tabs are for. - Counts are read with the label ("Variants 3"). A step adds its status after the label ("Regions, in progress");
statusLabeltranslates it. - Keyboard focus draws a teal ring on the tab, inside the row so a scrolling row never clips it. Panels take focus too, so the content is one Tab away.
- Disabled tabs are skipped by the arrow keys. Disabled steps stay readable, at slate rather than faded.
- Under reduced motion every shape jumps instead of gliding.
Props
Tabs passes activationMode and dir to Radix. On TabsList, ref and other props go to the tablist; className and style go to the scrolling row around it.
Tabs
| Prop | Type | Default | Description |
|---|---|---|---|
value | V extends string | None | The chosen tab, when you control it. Typed values stay typed. |
defaultValue | V | None | The tab chosen at first, when uncontrolled. |
onValueChange | (value: V) => void | None | Called with the newly chosen tab. |
activationMode | 'automatic' | 'manual' | 'automatic' | Manual: the arrows move focus, Enter or Space chooses. |
TabsList
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'line' | 'pill' | 'steps' | 'line' | Sections on a hairline, a compact switch, or the stages of a task. |
size | 'sm' | 'md' | 'lg' | 'md' | 30, 36 or 44px tall. |
fullWidth | boolean | false | Tabs share the width equally. Line and pill. |
aria-label | string | None | Names the tabs, when nothing nearby does. |
TabsTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
valueRequired | string | None | Matches its TabsContent. |
disabled | boolean | false | Cannot be chosen; skipped by the arrows. |
icon | ReactNode | None | Leading icon: 15, 16 or 18px by size. |
activeIcon | ReactNode | None | Icon while chosen, e.g. the duotone weight. |
count | number | string | None | A small badge after the label. Not shown on steps. |
status | 'not-started' | 'in-progress' | 'completed' | 'not-started' | Where the step stands. Steps only. |
statusLabel | string | None | What screen readers hear for the status: defaults to "in progress" and "completed". Steps only. |
TabsContent
| Prop | Type | Default | Description |
|---|---|---|---|
valueRequired | string | None | Matches its TabsTrigger. |
forceMount | true | None | Keep the panel mounted while hidden, e.g. to keep a form's state. |