From 17615935b91ee6aade438178e7c5b7562166f2dc Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Sat, 8 Aug 2026 06:02:38 +0200 Subject: [PATCH] fix(gui): keep brand colors on usage source icons in dark mode The usage filter marks were all run through a dark-theme invert filter, which shifted the brand hue of the Claude and Codex/OpenAI marks. Scope the invert to the monochrome Grok mark only, and add a regression test covering both explicit dark-theme and OS prefers-color-scheme paths. --- gui/src/pages/Usage.tsx | 2 +- gui/src/styles.css | 6 ++++-- gui/tests/usage-layout.test.ts | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/gui/src/pages/Usage.tsx b/gui/src/pages/Usage.tsx index afca8218f2..5169608983 100644 --- a/gui/src/pages/Usage.tsx +++ b/gui/src/pages/Usage.tsx @@ -235,7 +235,7 @@ function UsageFilters({ )} {choice === "grok" && ( - + )} {label} diff --git a/gui/src/styles.css b/gui/src/styles.css index 576122d8c2..6cb25f5386 100644 --- a/gui/src/styles.css +++ b/gui/src/styles.css @@ -2083,10 +2083,12 @@ button.prov-account-row.active { cursor: default; } .usage-segmented-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; border: none; background: transparent; color: var(--muted); padding: 4px 12px; border-radius: var(--radius-pill); cursor: pointer; font: inherit; white-space: nowrap; } .usage-segmented-btn.active { background: var(--raised); color: var(--text); font-weight: var(--weight-semibold); } .usage-source-mark { width: var(--icon-sm); height: var(--icon-sm); flex: 0 0 auto; object-fit: contain; } -:root[data-theme="dark"] .usage-source-mark { filter: invert(1); } +/* Only monochrome source marks (Grok) are inverted for dark themes. Brand-colored + marks (Claude, Codex/OpenAI) must keep their brand color; inverting shifts the hue. */ +:root[data-theme="dark"] .usage-source-mark--mono { filter: invert(1); } @media (prefers-color-scheme: dark) { - :root:not([data-theme="light"]) .usage-source-mark { filter: invert(1); } + :root:not([data-theme="light"]) .usage-source-mark--mono { filter: invert(1); } } @media (max-width: 760px) { diff --git a/gui/tests/usage-layout.test.ts b/gui/tests/usage-layout.test.ts index d8e8b53ee0..c34922a886 100644 --- a/gui/tests/usage-layout.test.ts +++ b/gui/tests/usage-layout.test.ts @@ -142,3 +142,24 @@ test("Usage renders Available history and a persistent qualification when histor } } }); + +test("Usage source marks keep brand colors and invert only the monochrome Grok mark", async () => { + const page = await Bun.file(new URL("../src/pages/Usage.tsx", import.meta.url)).text(); + const css = await Bun.file(new URL("../src/styles.css", import.meta.url)).text(); + + // Claude and Codex ship brand-colored SVGs and must not carry the mono modifier. + expect(page).toContain('src="/provider-icons/claude-color.svg"'); + expect(page).not.toContain('usage-source-mark usage-source-mark--mono" src="/provider-icons/claude-color.svg"'); + expect(page).toContain('src="/provider-icons/openai.svg"'); + expect(page).not.toContain('usage-source-mark usage-source-mark--mono" src="/provider-icons/openai.svg"'); + + // Grok ships a black monochrome mark: it is the only one that needs dark-theme inversion. + expect(page).toContain('usage-source-mark usage-source-mark--mono" src="/provider-icons/grok.svg"'); + + // Dark-theme inversion must be scoped to the mono modifier so brand hues survive. + expect(css).toContain(':root[data-theme="dark"] .usage-source-mark--mono { filter: invert(1); }'); + expect(css).not.toContain(':root[data-theme="dark"] .usage-source-mark { filter: invert(1); }'); + // The OS dark-mode (prefers-color-scheme) path must keep the same scoping. + expect(css).toContain(':root:not([data-theme="light"]) .usage-source-mark--mono { filter: invert(1); }'); + expect(css).not.toContain(':root:not([data-theme="light"]) .usage-source-mark { filter: invert(1); }'); +});