Tabs
Tab strips in three appearances: underline, segmented and pill.
import { Tabs, TabsList, Tab, … } from "@arcadia/ui"Three looks, one behaviour. underline, text tabs over the umbra line with a 1px indicator that moves on the morph spring, for settings and panels. segmented, the Segmented Control's thumb, for view switches. pill, the active tab a fill-3 pill switched instantly, for compact panel headers. A tab's icon draws its filled form while the tab is selected.
Tabs or a Segmented Control? Tabs switch between panels of content and carry the tab roles. To pick a value, such as a setting or a filter, with no panel of its own, use a Segmented Control.
Examples
rootStyle takes where the strip sits (margins, self-alignment, flex and grid placement, size bounds) and the knobs below, each a token of its kind. A colour, a raw length or any other property is a type error: a look that comes up twice becomes a variant.
| Knob | Takes | Default | Sets |
|---|---|---|---|
Ref<Control> | var(--control-lg) | Tab height. | |
Ref<Space> | var(--space-1-5) | Tab inline padding (an underline's first label hangs it outside). | |
Ref<Space> | var(--space-1) | Gap between tabs. | |
Paint | var(--text-3) | Tab label colour at rest. | |
Paint | var(--text-2) | Tab label colour under the pointer. | |
Paint | var(--text-1) | The current tab's label colour. | |
Paint | var(--text-4) | A disabled tab's label colour (the current tab keeps its own). |
The knobs go on TabsList and every tab inherits them. A disabled tab drops to --tab-fg-disabled; the current tab keeps its colour. A segmented strip takes the Segmented Control's knobs instead.
Tabs follow the APG pattern: a tablist with one tab stop, and panels labelled by their tabs. Focus never drops to the page when a tab is activated. A disabled tab stays in place at text-4 and is skipped by the arrow keys.
- ←→
- Move focus between tabs
- EnterSpace
- Activate the focused tab, unless
activateOnFocusalready did - HomeEnd
- First or last tab
Tabs
value- string
- The active tab (controlled).
defaultValue- string
- The initially active tab.
onValueChange- (value: string, details: { event: Event }) => void
- Called when the active tab changes.
orientation- "horizontal" | "vertical"Default"horizontal"
rootStyle- RootStyle
- Typed StyleX override: where the tabs sit.
className- string
- A product's hook class for its own layout rules. Being retired: new code places tabs with
rootStyle; the call sites left are counted by ui.lint (kit-classname) and only go down.
Rest props go to <div> props.
appearance- "underline" | "segmented" | "pill"Default"underline"
- underline: text tabs over a hairline, 1px indicator slides 200 ms (settings, panels). segmented: the SegmentedControl look (track + sliding thumb). pill: 24px tabs, the active one on a 6% fill, switched instantly (compact panel headers).
size- "sm" | "md"Default"md"
stretch- booleanDefaultfalse
- segmented only: tabs share the full width.
activateOnFocus- booleanDefaultfalse
- Activate a tab as soon as arrow keys focus it.
rootStyle- TabsListStyle
- Typed StyleX override: where the strip sits and its
--tab-*knobs (segmented: SegmentedControl's). className- string
- A product's hook class for its own layout rules. Being retired: new code places a strip with
rootStyle; the call sites left are counted by ui.lint (kit-classname) and only go down.
Rest props go to <div> props.
valuerequired- string
- Matches a TabsPanel's value.
disabled- boolean
- Text-4 and skipped.
icon- IconName
className- string
- A product's hook class for its own rules. Being retired: the call sites left are counted by ui.lint (
kit-classname) and only go down.
Rest props go to <button> props.
valuerequired- string
- The tab this panel belongs to.
className- string
- A product's hook class for its own rules. Being retired: the call sites left are counted by ui.lint (
kit-classname) and only go down.
Rest props go to <div> props.