diff --git a/src/client/components/Dailies.tsx b/src/client/components/Dailies.tsx index b191a95..97cf91d 100644 --- a/src/client/components/Dailies.tsx +++ b/src/client/components/Dailies.tsx @@ -194,8 +194,13 @@ function TickChip({ aria-pressed={isDone} aria-label={`${ariaLabel}${isDone ? ", done today" : ", not done today"}`} title={title} - className="flex max-w-[15rem] items-center gap-2 rounded-full border px-3 py-1.5 text-xs font-medium transition-colors" + // Done-ness for the stylesheet: the chip's own colours are the game's + // hue and arrive inline, which no rule can override, so the hover has to + // be drawn from what inline does not own — see `.tick-chip`. + data-done={isDone} + className="tick-chip flex max-w-[15rem] items-center gap-2 rounded-full border px-3 py-1.5 text-xs font-medium" style={{ + ["--hue" as string]: hue, borderColor: isDone ? hue : `color-mix(in srgb, ${hue} 20%, transparent)`, // Colour identifies the game; done-ness is carried by the tick, the // full-strength border and the wash. Keeping the label readable matters @@ -217,7 +222,7 @@ function TickChip({ opacity={isDone ? 1 : 0.25} /> - {label} + {label} {streak > 1 && ( {streak}d )} diff --git a/src/client/styles.css b/src/client/styles.css index a410337..0187d3f 100644 --- a/src/client/styles.css +++ b/src/client/styles.css @@ -295,6 +295,52 @@ body { filter 130ms ease-out; } +/* ---- Today's dailies ---------------------------------------------------- */ +/* + * A daily chip is the fastest thing on the page to act on, and it was saying + * none of that: the pointer stayed an arrow and nothing happened under it, so a + * strip of tappable jobs read as a row of status badges. + * + * The hover is drawn from what the component does *not* own. A chip's border, + * ink and wash are mixed from the game's hue, which is data rather than a token + * and therefore arrives as an inline style — and no rule here can override one + * of those. So the cursor, a ring in the same hue, a one-pixel lift and the + * label coming up to full ink carry it instead. `--hue` and `data-done` are the + * two things the chip hands over for that. + * + * The label only brightens while the job is outstanding: on a finished chip the + * ink *is* the game's hue, and overriding it with `ink` would trade the one + * thing that says which game for a hover state. + */ +.tick-chip { + transition: + border-color 130ms ease-out, + background-color 130ms ease-out, + color 130ms ease-out, + box-shadow 130ms ease-out, + transform 120ms cubic-bezier(0.2, 0.7, 0.3, 1); +} + +.tick-label { + transition: color 130ms ease-out; +} + +@media (hover: hover) { + .tick-chip:hover { + transform: translateY(-1px); + box-shadow: 0 0 0 1px color-mix(in srgb, var(--hue) 45%, transparent); + } + .tick-chip[data-done="false"]:hover .tick-label { + color: var(--color-ink); + } +} + +/* Press feedback on both mouse and touch, as the event row has. */ +.tick-chip:active { + transform: scale(0.96); + transition-duration: 60ms; +} + @media (prefers-reduced-motion: reduce) { .event-row, .row-rail, @@ -302,13 +348,17 @@ body { .row-count, .row-summary, .row-check, - .meter-tick { + .meter-tick, + .tick-chip, + .tick-label { transition: none; } .row-check:hover, .row-check:active, .event-row:hover .row-open, - .event-row:hover .row-rail { + .event-row:hover .row-rail, + .tick-chip:hover, + .tick-chip:active { transform: none; } }