mirror of
https://github.com/block/buzz.git
synced 2026-11-13 04:41:12 +00:00
The old name described one variant's appearance — a segmented control — which is exactly why a second appearance felt like it needed a second component. `Tabs` names the behaviour, so both variants sit under it naturally. Base UI's own export is also `Tabs`, so it is imported as `BaseTabs`; that reads better anyway, since `BaseTabs.Root` says plainly which layer each part comes from. `variant="chrome" | "panel"` is the chrome/surface axis DESIGN.md had deferred until a second component made the case. `IconButton` made it once; this made it twice, on a real screen. The failure: the chrome container is `glass-2`, which over a white panel composites to pure white, and its selected pill is `neutral-1` — also pure white. Container and selection became one colour with only a shadow between them, and no guard could see it because the component had no way to state which background it expected. Retinting `--bg-chrome-selected` would only have moved the collision. One component with a variant rather than two components, because behaviour, keyboard model, accessibility, props, and the Base UI parts underneath are identical — a sibling would duplicate all of it to change how selection is drawn, and the two would drift exactly as the four hand-assembled chrome surfaces did. The panel variant still emits `role="tablist"`, correct `aria-selected`, roving `tabIndex`, and arrow-key navigation, which is the concrete payoff. Each variant states all three of its states in full rather than inheriting a colour and overriding part of it. When a shared rule and a variant rule both set a property, the more specific selector wins regardless of source order, and a variant rule is always more specific — so the shared `[data-selected]` rule lost and the selected tab silently rendered as unselected. Hover carries `:not([data-selected])` because hover and selected genuinely overlap: a cursor can sit on the selected tab, and DESIGN.md § State says a selected item in a toggle group is not interactive. Those two selectors score identical specificity, so without the exclusion only source order would keep it right. This follows Chrome's modern CSS guidance: use `:not()` to say a state is irrelevant, never to defend one rule from another. `WorkspaceSurface` is renamed to `Panel` in the same change, since both touch the same rules in components.css. Signed-off-by: Morgan Martin <morganmartin@squareup.com> Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>