fix(player): marks are weighed when answered, lit and pressed hold under the pointer, and Restart is one glyph

The watched toggle weighed its own click at click time against marks stamped when the server answered, so the late answer of its previous click overruled the newer one; it now holds its word while any of its writes is on the way, is stamped at the answer, and drops a refused write. The hosts return the write. In a live search the feed's own answered writes stand down over the rows, which carry the marks, so a newer mark from the related list reaches a card the feed touched earlier.

A lit mode cycle got the hover layer, and a variant's hover colour outranked the pressed and lit colours: the state layer stands down on [data-lit] and those colours are important. The menus' Play from the beginning wears RotateCcw like the card's Restart, Reset wears Eraser and Redownload the retry arrow (DESIGN decision log). Tests pin the toggle's orders, the Watch later mark at its call sites, the live-search overrides, the hover exemptions and the menu glyphs.
This commit is contained in:
2026-10-25 13:51:26 +01:00
parent 13f65b4844
commit 0bc1024287
18 changed files with 629 additions and 103 deletions
+10 -7
View File
@@ -57,14 +57,16 @@ const BUTTON_VARIANTS: Record<ButtonVariant, string> = {
};
// A pressed toggle (`pressed`) is the solid accent in every variant (B3: "selected stays the solid
// accent"); the layer utilities leave it alone, so it stays solid under the pointer too.
// accent"); the layer utilities leave it alone, and its colours are marked important (`!`) because a
// variant's hover colour (`act:hover:text-fg`, the overlay's `act:hover:bg-white/15`) is the more
// specific selector — without it the accent turned grey, or white, under the pointer.
const PRESSED =
"aria-pressed:bg-accent aria-pressed:text-accent-fg aria-pressed:border-accent aria-pressed:no-underline";
"aria-pressed:bg-accent! aria-pressed:text-accent-fg! aria-pressed:border-accent! aria-pressed:no-underline!";
// A mode cycle that is not off (`lit`: auto-advance Next, loop One) looks pressed but is no toggle —
// its name says the mode, and aria-pressed would announce a two-state switch whose name changes on
// every press. Keyed to an attribute like PRESSED, so it outranks the variant's own colours.
// every press. The same look as PRESSED, under the pointer too: the layers stand down on `[data-lit]`.
const LIT =
"data-lit:bg-accent data-lit:text-accent-fg data-lit:border-accent data-lit:no-underline";
"data-lit:bg-accent! data-lit:text-accent-fg! data-lit:border-accent! data-lit:no-underline!";
// B1, the whole ladder: explicit heights, so a toolbar's `sm` button, search pill, dropdown trigger and
// small icon button all line up at 28. A call site picks a rung; it does not invent one. rem-derived, so
@@ -125,9 +127,10 @@ const ICON_VARIANTS: Record<IconButtonVariant, string> = {
};
// An icon toggle that is on shows it in the glyph (watched, saved, a selected track), not as a solid
// fill: a solid accent square in a row of quiet glyphs outshouts the row's primary action.
const ICON_PRESSED = "aria-pressed:text-chosen";
const ICON_LIT = "data-lit:text-chosen";
// fill: a solid accent square in a row of quiet glyphs outshouts the row's primary action. Important
// for the same reason as PRESSED: the variant's hover colour would take the tint off under the pointer.
const ICON_PRESSED = "aria-pressed:text-chosen!";
const ICON_LIT = "data-lit:text-chosen!";
const ICON_SIZES: Record<IconButtonSize, string> = {
sm: "size-7 [&>svg]:size-4",