fix(player): what is chosen on the player chrome is marked amber, not written in the accent

A light theme's accent as text on the always-dark chrome was 2.84:1 (Slate). `--chosen` is the
accent unless a scope names its own; `media-marks` (was media-focus) points it, with the focus
ring, at the media amber. A chosen track stays white with an amber tick (MenuCheck reads
`text-chosen`); a chosen settings tab or toggle is white with an amber underline (MEDIA_CHOSEN).
The theme contract test holds the token, the tick and the player components free of accent text.
This commit is contained in:
2026-10-10 21:56:47 +02:00
parent e1d325b942
commit 8312c6d592
13 changed files with 85 additions and 49 deletions
+21 -6
View File
@@ -81,18 +81,33 @@ describe("the media colours", () => {
expect(field).not.toMatch(/\bfocus-within:(border|ring)-accent\b/);
});
it("every surface painted the player chrome names its focus colour", () => {
const at = css.indexOf("@utility media-focus {");
expect(css.slice(at, css.indexOf("\n}", at))).toMatch(/--focus:\s*var\(--media-mark\);/);
// A class string that paints the chrome carries the media focus (media-scope re-points it too):
// the theme's accent ring fell under 3:1 on it in a light scheme.
it("every surface painted the player chrome names its marks: the focus ring and what is chosen", () => {
const block = (name: string) => {
const at = css.indexOf(name);
return css.slice(at, css.indexOf("\n}", at));
};
expect(block("@utility media-marks {")).toMatch(/--focus:\s*var\(--media-mark\);/);
expect(block("@utility media-marks {")).toMatch(/--chosen:\s*var\(--media-mark\);/);
expect(block("@utility media-scope {")).toMatch(/--chosen:\s*var\(--media-mark\);/);
expect(block("@theme inline {")).toMatch(/--color-chosen: var\(--chosen, var\(--accent\)\);/);
// The tick of a chosen row reads the chosen colour, so a scope can name its own.
const check = readFileSync(new URL("../components/ui/MenuCheck.tsx", import.meta.url), "utf8");
expect(check).toMatch(/\btext-chosen\b/);
// The player's own components are chrome throughout: nothing chosen there is the accent's text
// (a light theme's accent is dark on it — Slate 2.84:1).
for (const f of ["PlexPlayer.tsx", "PlexPlayerSettings.tsx", "UpNextCard.tsx"]) {
const src = readFileSync(new URL(`../components/${f}`, import.meta.url), "utf8");
expect(src, f).not.toMatch(/\btext-accent\b/);
}
// A class string that paints the chrome carries the media marks (media-scope re-points them
// too): the theme's accent ring fell under 3:1 on it in a light scheme.
const root = new URL("../", import.meta.url);
const offenders: string[] = [];
for (const f of readdirSync(root, { recursive: true, encoding: "utf8" })) {
if (!/\.tsx?$/.test(f) || /\.test\.tsx?$/.test(f)) continue;
const src = readFileSync(new URL(f.replace(/\\/g, "/"), root), "utf8");
for (const m of src.matchAll(/["'`]([^"'`]*\bbg-media-chrome\b[^"'`]*)["'`]/g))
if (!/\bmedia-(scope|focus)\b/.test(m[1]!)) offenders.push(`${f}: ${m[1]}`);
if (!/\bmedia-(scope|marks)\b/.test(m[1]!)) offenders.push(`${f}: ${m[1]}`);
}
expect(offenders).toEqual([]);
});