From b448a4e24619ff36e902e37bd61fa1950b5e2fa4 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Sun, 19 Apr 2026 17:12:01 -0400 Subject: [PATCH] fix(theme): contrast on primary buttons and muted text in light mode - introduce --color-on-primary (navy-950 in dark, white in light) so text on bg-teal-500 reads clearly regardless of mode; swap hardcoded text-navy-950 occurrences on teal fills for text-on-primary - darken --color-fg-muted in light mode from slate-400 (#94a3b8) to slate-500 (#64748b); slate-400 failed AA on the tinted page - deepen page tint to #eef2f7 and strengthen line/line-strong so white cards have real edges, not invisible hairlines Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/fabro-web/app/app.css | 19 ++++++++++++------- apps/fabro-web/app/components/ui.tsx | 2 +- apps/fabro-web/app/install-app.tsx | 4 ++-- apps/fabro-web/app/routes/run-detail.tsx | 2 +- apps/fabro-web/app/routes/start.tsx | 2 +- lib/crates/fabro-spa/assets/assets/app.css | 2 +- ...{entry-gcen40sq.css => entry-g7fj8z7g.css} | 2 +- .../{entry-nsnsy3sn.js => entry-h24h4gjh.js} | 6 +++--- lib/crates/fabro-spa/assets/index.html | 4 ++-- 9 files changed, 24 insertions(+), 19 deletions(-) rename lib/crates/fabro-spa/assets/assets/{entry-gcen40sq.css => entry-g7fj8z7g.css} (88%) rename lib/crates/fabro-spa/assets/assets/{entry-nsnsy3sn.js => entry-h24h4gjh.js} (92%) diff --git a/apps/fabro-web/app/app.css b/apps/fabro-web/app/app.css index 59a03280e..f0c5bc131 100644 --- a/apps/fabro-web/app/app.css +++ b/apps/fabro-web/app/app.css @@ -53,6 +53,10 @@ --color-line: rgba(255, 255, 255, 0.06); --color-line-strong: rgba(255, 255, 255, 0.12); --color-focus: rgba(103, 178, 215, 0.4); + /* Text color that sits on --color-teal-500 primary fills. + Dark mode: teal-500 is light (#67B2D7) so dark text reads best. + Light mode: teal-500 is dark (#0e7490) so white reads best. */ + --color-on-primary: #0F1729; } /* ── Light theme overrides ── */ @@ -60,18 +64,19 @@ .light { /* Semantic tokens — page is softly tinted so panels/cards (white) read as the lighter surface, mirroring the dark-theme hierarchy. */ - --color-page: #f3f6fa; + --color-page: #eef2f7; --color-panel: #ffffff; - --color-panel-alt: #e9eef5; - --color-overlay: rgba(15, 23, 41, 0.04); - --color-overlay-strong: rgba(15, 23, 41, 0.08); + --color-panel-alt: #e4eaf1; + --color-overlay: rgba(15, 23, 41, 0.05); + --color-overlay-strong: rgba(15, 23, 41, 0.10); --color-fg: #0f172a; --color-fg-2: #1e293b; --color-fg-3: #475569; - --color-fg-muted: #94a3b8; - --color-line: rgba(15, 23, 41, 0.08); - --color-line-strong: rgba(15, 23, 41, 0.14); + --color-fg-muted: #64748b; + --color-line: rgba(15, 23, 41, 0.10); + --color-line-strong: rgba(15, 23, 41, 0.18); --color-focus: rgba(14, 116, 144, 0.5); + --color-on-primary: #ffffff; /* Accent — darkened for WCAG AA on white */ --color-mint: #0d9488; diff --git a/apps/fabro-web/app/components/ui.tsx b/apps/fabro-web/app/components/ui.tsx index d50ff61cf..064226779 100644 --- a/apps/fabro-web/app/components/ui.tsx +++ b/apps/fabro-web/app/components/ui.tsx @@ -6,7 +6,7 @@ export const INPUT_CLASS = "block w-full rounded-lg bg-panel-alt px-3.5 py-2.5 text-base text-fg outline-1 -outline-offset-1 outline-white/10 placeholder:text-fg-muted focus:outline-2 focus:-outline-offset-1 focus:outline-teal-500 sm:text-sm"; export const PRIMARY_BUTTON_CLASS = - "inline-flex items-center justify-center gap-2 rounded-lg bg-teal-500 px-4 py-2 text-sm font-medium text-navy-950 transition-colors hover:bg-teal-300 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-500 disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:bg-teal-500"; + "inline-flex items-center justify-center gap-2 rounded-lg bg-teal-500 px-4 py-2 text-sm font-medium text-on-primary transition-colors hover:bg-teal-300 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-500 disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:bg-teal-500"; export const SECONDARY_BUTTON_CLASS = "inline-flex items-center justify-center gap-2 rounded-lg bg-transparent px-3.5 py-2 text-sm font-medium text-fg-2 outline-1 -outline-offset-1 outline-white/10 hover:bg-overlay hover:text-fg focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-teal-500"; diff --git a/apps/fabro-web/app/install-app.tsx b/apps/fabro-web/app/install-app.tsx index 240f90de7..0238a4df2 100644 --- a/apps/fabro-web/app/install-app.tsx +++ b/apps/fabro-web/app/install-app.tsx @@ -708,9 +708,9 @@ function Stepper({ const isLast = index === STEPPER_STEPS.length - 1; const isLinkable = isComplete || isCurrent; const circleClass = isComplete - ? "bg-mint text-navy-950" + ? "bg-mint text-on-primary" : isCurrent - ? "bg-teal-500 text-navy-950" + ? "bg-teal-500 text-on-primary" : "bg-overlay text-fg-muted outline-1 -outline-offset-1 outline-white/10"; const labelClass = isCurrent ? "text-fg" diff --git a/apps/fabro-web/app/routes/run-detail.tsx b/apps/fabro-web/app/routes/run-detail.tsx index 6273a9d24..a0bacd12c 100644 --- a/apps/fabro-web/app/routes/run-detail.tsx +++ b/apps/fabro-web/app/routes/run-detail.tsx @@ -113,7 +113,7 @@ export default function RunDetail({ loaderData, params }: any) {