Tabs

A tab strip in two flavours - a filled pill on a muted track, and a bare line with a 2px rule under whichever tab is selected.

Two components, 31 variants

Using the Tabs

What is the difference between the two variants?
Where the selection lives, and how tall the trigger has to be. Default lifts the active tab onto a white card - base.background, a 1px base.input edge and a small drop shadow - inside a base.muted track with radius.lg, and its trigger is 24. Line throws the track away: no fill, no radius, and the selection is a 2px base.foreground rule along the bottom of the trigger, which grows to 36 so the rule clears the label. Unselected tabs look identical in both, which is why they read as the same component.
Why are there 28 trigger variants and not 32?
Because Active On has no Hover, in either variant, with or without an icon. A tab that is already selected has nothing to promise when you point at it, so the file leaves those four cells out rather than drawing something arbitrary. Everything else is a full 2 × 2 × 4 × 2 grid: two variants, on and off, Default, Hover, Focus and Disabled, and the icon on or off.
What does Hover actually change?
The label and only the label, from base.muted-foreground to base.foreground. There is no hover surface behind an unselected tab in either variant, which keeps the selected tab the only thing carrying any weight. Disabled is different again - the whole variant drops to 50% rather than recolouring anything.
Where does the underline sit?
Along the bottom edge of the trigger, as a 2px rectangle the full width of it, bound to base.foreground. That is why the Line trigger is 36 tall where the filled one is 24: the rule needs air under the label, and Line has no track to supply it. The gap comes to 6 under the label's box and 8 under the icon, which is close to the source's own 5 below the trigger - and 36 is exactly the h-9 the source gives its line list.
How do I put an icon in a tab?
Set Show Icon to Yes on the trigger. It is a real axis rather than a layer you toggle, so an instance either carries the glyph or it does not. The icon is 16 × 16, sits before the label with 6 between them, and takes the trigger from 74 to 96. It is bound to the same token as the label - base.foreground when the tab is on or hovered, base.muted-foreground when it is not - so the two can never drift apart. The component ships Tabler's app-window as its placeholder; swap the glyph on the instance.
Should every tab in a strip have one?
Either all of them or none. A strip where two tabs have icons and two do not reads as two groups, and the eye finds the odd one out before it reads any label. What to avoid entirely is an icon with no label: a tab strip is a set of destinations, and a row of bare glyphs asks the reader to guess at each one. If the labels genuinely do not fit, the strip has too many tabs rather than too much text.
How do I do a vertical line strip?
Not from the file - the Tabs component publishes three of its four combinations and Line / Vertical is the one missing. In the source, a vertical line strip moves the 2px rule to the inline end of the trigger rather than under it. Draw it by hand or ask for the fourth combination; the trigger itself needs nothing new.
Is there a component for the panel?
No, and there should not be. The source has four parts - Tabs, TabsList, TabsTrigger, TabsContent - and the kit draws the middle two. A panel component with a fixed size would be wrong in every layout but the one it was drawn for, so the panel in the usage frame is a plain board you are meant to replace.
How many tabs is too many?
Six is the ceiling and four is comfortable. Past that the labels either squeeze past readability, hide behind a horizontal scroll nobody discovers, or get folded into a select - and a select defeats the one thing tabs are for, which is seeing the options without clicking. Eight peers is a page that wants splitting, not a strip that wants scrolling.
Does it work right to left?
Yes, with dir on an ancestor and nothing else. The strip is a row of peers in reading order, so it reverses on its own, and the underline is a child of its trigger anchored to both inline edges - it moves because the trigger moves. Never add flex-row-reverse on top; it cancels the dir out. The two things that do need thought are the arrow keys, which follow the visual direction, and a vertical line strip's rule, which belongs on the inline end.