fix(design): the button and chip hover colours were never generated — an act: variant instead of a runtime prefix (E28)

buttonStyles/chipStyles glued the hover guard on at runtime (`${ACT}hover:text-fg`). Tailwind emits CSS
only for a class it finds whole in the source, so all 22 guarded hovers shipped with no rule: no text or
edge colour on hover for ghost/outline/danger buttons, no white layer on the player's control row, no
underline on a link button, no accent hover on token/suggest/ghost chips. The stylesheet had 0 rules
for `not-aria-disabled`.

- `@custom-variant act` (index.css): the same guard as the state layers; `:disabled` never matches an
  <a>, so one variant serves buttons and links and the per-element variant maps go.
- ui/tailwindScan.contract.test.ts: every class the primitives can emit stands whole in their source.
- a lint rule refuses a variant right after an interpolation; the `enabled:hover:` rule now points at
  `act:hover:`.
This commit is contained in:
2026-09-25 14:13:51 +02:00
parent ff80cc96fd
commit cc8e0648db
7 changed files with 144 additions and 48 deletions
+7
View File
@@ -75,6 +75,13 @@
/* ===== E28 tokens as utilities (docs/DESIGN.md) — the ONE source the ui/ primitives and the design
lab both read, so a decided value is never restated as an arbitrary class. ===== */
/* B3: `act:` — a hover or press that answers only a control that can act: not a disabled one, not a busy
one (FocusSafeButton's aria-disabled). `:disabled` never matches an <a>, so one variant serves a
button and a link alike. A literal variant, never a prefix glued on at runtime: Tailwind emits CSS
only for a class it finds WHOLE in the source, and the `${ACT}hover:…` the primitives used to build
shipped with no rule at all (E28 audit; ui/tailwindScan.contract.test.ts). */
@custom-variant act (&:not(:disabled, [aria-disabled="true"]));
/* B3: a transparent control's hover / pressed layer. The layer answers only a control that can act:
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. */