From 663a7e8012815bf1e151cc927d25844479d12804 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sat, 10 Oct 2026 22:56:09 +0200 Subject: [PATCH] fix(player): the Plex player's fills hold on its chrome: an amber head on a white played part A light theme's accent as a fill on the always-dark chrome was ~2.8:1 (Slate), under the 3:1 a control needs. A native checkbox and slider tint with `accent-chosen` (the accent elsewhere, the media amber under `media-marks`), and so does the skip button's countdown. The progress bar plays white with an amber head: amber there marks a place, as the intro / credits markers do, and an amber played part made a marker ahead of the head read as already played (owner's choice of three). --- docs/DESIGN.md | 5 ++++- frontend/src/components/PlexPlayer.tsx | 11 +++++++---- frontend/src/components/ui/NativeControls.tsx | 7 ++++--- frontend/src/lib/theme.contract.test.ts | 12 ++++++++++++ 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 4fa263bd..53680f95 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -118,7 +118,10 @@ style guide. player (the Plex player, the YouTube stage) and on every surface painted `--media-chrome` (its menus, panels and bars), and a form there (`media-scope`) re-points `--danger` too. A chosen item there is white like the rest, marked by an amber tick (a track) or underline (a tab, `ui/mediaChrome`), never - the accent as text; a choice filled with the accent keeps it, its own text holds on it. + the accent as text; a choice filled with the accent keeps it, its own text holds on it. A fill that + must be seen there — a native checkbox or slider (`accent-chosen`), the skip button's countdown — is + the amber too; the Plex progress bar plays white with an amber head, so an amber marker ahead of + the head never reads as already played. `theme.contract.test.ts` measures the red on the chrome and holds every class string that paints it to name one of the two. Black and white are the media scrims and their text. Five schemes × dark and light (B2): **Midnight** (blue), **Forest** (cyan), **Slate** (fuchsia), **Matrix** (moss) each earn their diff --git a/frontend/src/components/PlexPlayer.tsx b/frontend/src/components/PlexPlayer.tsx index 8a313c36..c751e82d 100644 --- a/frontend/src/components/PlexPlayer.tsx +++ b/frontend/src/components/PlexPlayer.tsx @@ -1429,12 +1429,12 @@ export default function PlexPlayer({ itemId, onClose, queue, fromStart = false } <> {/* draining fill across the whole button… */} {/* …plus a crisp bottom bar so the countdown is unmistakable */} @@ -1542,13 +1542,16 @@ export default function PlexPlayer({ itemId, onClose, queue, fromStart = false } }} /> ))} + {/* The played part white, the head the media amber: amber is a marked place on + the chrome (the head, the markers above), and an amber played part made a + marker ahead of the head read as already played. */}
{/* The head: on hover for a mouse, always on touch (C3), where nothing hovers. */}
` for any of them anywhere else is a lint error. @@ -22,7 +23,7 @@ export function Checkbox({ type="checkbox" onChange={(e) => onChange?.(e.target.checked)} className={clsx( - "size-4 shrink-0 cursor-pointer accent-accent disabled:cursor-not-allowed", + "size-4 shrink-0 cursor-pointer accent-chosen disabled:cursor-not-allowed", className, )} {...rest} @@ -70,7 +71,7 @@ export function Slider({ type="range" onChange={(e) => onChange(Number(e.target.value))} className={clsx( - "min-h-6 w-full cursor-pointer accent-accent pointer-coarse:min-h-(--touch-target)", + "min-h-6 w-full cursor-pointer accent-chosen pointer-coarse:min-h-(--touch-target)", className, )} {...rest} diff --git a/frontend/src/lib/theme.contract.test.ts b/frontend/src/lib/theme.contract.test.ts index b48e016e..6628b61e 100644 --- a/frontend/src/lib/theme.contract.test.ts +++ b/frontend/src/lib/theme.contract.test.ts @@ -100,6 +100,18 @@ describe("the media colours", () => { ); expect(buttons).toMatch(/aria-pressed:text-chosen/); expect(buttons).toMatch(/aria-expanded:text-chosen/); + // A native checkbox's and slider's fill is the chosen colour too (the player's settings), and so + // is the skip button's countdown: a fill that must be seen on the chrome. + const native = readFileSync( + new URL("../components/ui/NativeControls.tsx", import.meta.url), + "utf8", + ); + expect(native).not.toMatch(/\baccent-accent\b/); + expect(native.match(/\baccent-chosen\b/g)).toHaveLength(2); + const plex = readFileSync(new URL("../components/PlexPlayer.tsx", import.meta.url), "utf8"); + // No accent fill at all on the Plex player: its progress bar's played part is white and its head + // the media amber — a marked place, like the intro / credits markers and the clip's playhead. + expect(plex).not.toMatch(/\bbg-accent\b/); // (A pressed FILLED button keeps the accent fill under its own `text-accent-fg`.) expect(buttons).not.toMatch(/aria-(pressed|expanded):text-accent(?!-)/); // The player's own components are chrome throughout: nothing chosen there is the accent's text