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); }');
+});