fix(ui): Tabs is an ARIA tab list with roving focus and a wired panel

The rail was a row of aria-pressed toggles: every tab a Tab stop, no arrows,
nothing tying a tab to its content. tablist/tab/aria-selected, one Tab stop,
arrows on either axis, Home/End, and useTabPanel for aria-controls/labelledby.
This commit is contained in:
2026-09-27 14:22:37 +02:00
parent a975ab627a
commit 922d589b09
14 changed files with 311 additions and 139 deletions
+6 -5
View File
@@ -89,10 +89,10 @@
not a disabled one, not a busy one (FocusSafeButton's aria-disabled), and not a pressed toggle, whose
solid accent is its state and must not turn grey under the pointer. */
@utility state-layer {
&:hover:not(:disabled, [aria-disabled="true"], [aria-pressed="true"]) {
&:hover:not(:disabled, [aria-disabled="true"], [aria-pressed="true"], [aria-selected="true"]) {
background-color: var(--hover);
}
&:active:not(:disabled, [aria-disabled="true"], [aria-pressed="true"]) {
&:active:not(:disabled, [aria-disabled="true"], [aria-pressed="true"], [aria-selected="true"]) {
background-color: var(--press);
}
}
@@ -674,8 +674,8 @@ body {
The ~14 hand-rolled "this is the selected / current / active one" idioms all painted the SAME
bg-accent fill via an inline ternary (`sel ? "bg-accent text-accent-fg border-accent" : "…"`),
which drifted over time. These recipes OWN that paint, keyed on the semantic ARIA the control
already carries — the JSX just toggles `aria-pressed` (a segment/toggle) or `aria-current` (the
single current item) and the CSS paints, so one control's selected look can never diverge from
already carries — the JSX just toggles `aria-pressed` (a segment/toggle), `aria-selected` (a tab,
ui/Tabs) or `aria-current` (the single current item) and the CSS paints, so one control's selected look can never diverge from
the next's. Geometry (padding / radius / border-width / font) stays a per-site utility since it
legitimately varies. NO focus-visible here on purpose: the `@layer base` rule above already rings
every button/a/[role]; re-adding it would duplicate that (an R9 review finding). Because these sit
@@ -705,7 +705,8 @@ body {
}
/* selected — SOLID accent fill (the dominant idiom). (The nav rail's "you are here" is ui/NavRail's
tint + edge bar since E28 S3d; its old `.is-current` recipe is gone.) */
.pill[aria-pressed="true"] {
.pill[aria-pressed="true"],
.pill[role="tab"][aria-selected="true"] {
background-color: var(--accent);
color: var(--accent-fg);
border-color: var(--accent);