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).
This commit is contained in:
2026-10-10 22:56:09 +02:00
parent 93192dadd8
commit 663a7e8012
4 changed files with 27 additions and 8 deletions
+7 -4
View File
@@ -1429,12 +1429,12 @@ export default function PlexPlayer({ itemId, onClose, queue, fromStart = false }
<>
{/* draining fill across the whole button… */}
<span
className="absolute inset-y-0 left-0 bg-accent/50"
className="absolute inset-y-0 left-0 bg-chosen/50"
style={{ width: `${skipProgress * 100}%` }}
/>
{/* …plus a crisp bottom bar so the countdown is unmistakable */}
<span
className="absolute bottom-0 left-0 h-1 bg-accent"
className="absolute bottom-0 left-0 h-1 bg-chosen"
style={{ width: `${skipProgress * 100}%` }}
/>
</>
@@ -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. */}
<div
className="absolute inset-y-0 left-0 bg-accent rounded-full"
className="absolute inset-y-0 left-0 bg-white rounded-full"
style={{ width: `${headPct}%` }}
/>
{/* The head: on hover for a mouse, always on touch (C3), where nothing hovers. */}
<div
className={`absolute top-1/2 -translate-y-1/2 -translate-x-1/2 w-3 h-3 rounded-full bg-accent transition-opacity ${
className={`absolute top-1/2 -translate-y-1/2 -translate-x-1/2 w-3 h-3 rounded-full bg-media-mark transition-opacity ${
scrub != null
? "opacity-100"
: "opacity-0 group-hover:opacity-100 [@media(hover:none)]:opacity-100"
@@ -2,7 +2,8 @@ import clsx from "clsx";
import type { InputHTMLAttributes, ReactNode, Ref } from "react";
// A6 (docs/DESIGN.md): checkbox, slider and colour well are the BROWSER's controls, tinted
// with the accent — keyboard, screen reader and forced-colours support come free, and a drawn copy
// with the chosen colour (the accent; the media amber on the player chrome) — keyboard, screen
// reader and forced-colours support come free, and a drawn copy
// would have to rebuild all three. What the browser does not give is the 44 touch target: the labelled
// row (`CheckRow`) or the slider's own box grows to it on a coarse pointer. Like `Input`, a raw
// `<input>` 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}
+12
View File
@@ -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