Tabs
One list of tabs, one panel showing. Three looks over the same behaviour: an underline for the sections of a page, a segmented track for views of one object, and pills for filtered views of a list.
Try it
Consultation with Dr Marta Reis, Tuesday at 14:30, room 2. Forty minutes.
Focus a tab and press the arrows. Home and End go to the ends, Files is disabled and is skipped, and Tab moves on to the panel.
Choosing a variant
Underline divides a page or a large panel into sections. It sits on the surface with a rule beneath it, takes as many tabs as the width allows, and scrolls when it runs out. Segmented switches between two to five views of one object inside a card or a dialog: the track has a fixed share for each label, so more than five crowd it and a long label is cut. Pill filters a list, and is the one to use when the count matters as much as the label.
Tabs are for views the reader moves between freely, each complete on its own. For more than a handful of views that do not fit a row, a Select over a single panel reads better. For steps in an order, tabs promise a freedom the flow does not have. And a control that holds a value and shows no panel is a radio group, however much it looks like the segmented track: that is the SegmentedControl, which shares this track’s stylesheet.
The three variants
Consultation with Dr Marta Reis, Tuesday at 14:30, room 2. Forty minutes.
Leonor Viana, Marta Reis and an interpreter. All three have confirmed.
Seventy-two appointments this week.
Anatomy
| Part | Underline | Segmented | Pill |
|---|---|---|---|
| Height | 40, ghost 32 | 32, segment 28 | 32 |
| Inline padding | 12 | equal columns | 16; 16 and 4 with a count |
| Gap between tabs | 4 | 2 | 8 |
| Radius | ghost 8 | track 8, thumb 6 | full |
| Label, Semibold | 14 / 18 | 12 / 16 | 14 / 22 |
The underline tab’s hover is a rounded ghost inside the tab rather than the tab’s whole box: 32 inside 40 leaves 4 above and below, so the wash touches neither the rule under the list nor the bar of the selected tab. The whole 40 still takes the click. The focus ring follows the ghost for the same reason.
The segmented thumb is one element that slides. The columns are equal, so a step is the thumb’s own width plus the gap and nothing is measured in script. It moves in the travel duration and jumps under reduced motion. The underline’s bar fades instead: its tabs have different widths, and sliding it would mean measuring them.
Where it leaves the drawing
Segmented and pill are drawn; the underline is not, and was added because a page needs one. Its selected label is the primary text colour, not the accent the segmented label takes: the accent is the colour of a link, and an underline tab sits beside real ones.
The segmented track is drawn in the canvas colour with no edge, on a white artboard. On the canvas it disappears, so it takes the sunken surface and a hairline; in dark the sunken surface is the canvas and the hairline alone carries it, while the thumb takes the overlay surface, a step above a card and two above the track. The thumb’s shadow was a loose value and became the smallest elevation step, which the elevation file had been keeping a place for. The radii are 8 outside and 6 inside, concentric over 2 of padding, where the drawn 10 is not on the scale.
The selected pill’s count was drawn white on the palest accent and did not read; the fill is kept and the number takes the accent text colour. In dark an unselected pill takes a border, as the Badge does, because its fill is the canvas there.
States
Documents.
A list that does not fit scrolls, and the selected tab is kept in view. The scrolling is the list’s own: the page never moves to follow a tab. A disabled tab stays in the accessibility tree with aria-disabled, so a reader can learn that it exists; the arrows pass over it and a click does nothing.
Only the panel that is showing is mounted. keepMounted renders every panel and hides the rest, for a form half filled in another tab. A value that names no tab, or a disabled one, shows the first tab that can be selected rather than an empty panel.
The day view.
Accessibility
The WAI-ARIA tabs pattern. The list is one tab stop, the selected tab; the arrows move within it. By default moving focus selects, because a panel here is already rendered and costs nothing to show. activation="manual" makes the arrows move focus only, for a panel that has to fetch. The panel can take focus, so a panel of plain text is not skipped by Tab.
| Key | Does |
|---|---|
→ ← | The next and the previous tab, wrapping at the ends and skipping a disabled one. Reversed under dir="rtl". |
Home End | The first and the last tab that can be selected. |
Enter Space | Selects the focused tab. Only needed when activation is manual. |
Tab | Leaves the list for the panel. The list is one stop, whichever tab is selected. |
The selected tab never differs by colour alone: the underline gains a bar, the segment a lifted thumb, the pill an inverted fill. The count is part of the tab’s name, read as “Participants 3”. Up and down arrows are left to the page, since the list is horizontal.
Props
| Prop | Type | Default |
|---|---|---|
items | readonly Tab | required |
label | string | required |
variant | 'underline' | 'segmented' | 'pill' | 'underline' |
value | string | — |
defaultValue | string | the first enabled item |
onChange | (id: string) => void | — |
activation | 'automatic' | 'manual' | 'automatic' |
keepMounted | boolean | false |
fullWidth | boolean | false |
| Prop | Type | Default |
|---|---|---|
id | string | required |
label | string | required |
content | React | required |
count | number | — |
disabled | boolean | false |