From d18bb612dfa2a8a67733170f4168f61c78be9011 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Sep 2026 16:17:24 -0400 Subject: [PATCH] fix(frontend): readability in both themes - Token colors wrapped in color-mix so opacity modifiers (bg-surface/90 etc.) generate rules; map overlay panels were rendering see-through. - Light mode: keep white text on saturated fills (primary/danger buttons were remapped to navy); map text-gray-200, bg-gray-800/60, border-gray-600. - Dark mode: lift text-gray-600/700 to gray-500 for contrast. Co-Authored-By: Claude Opus 5.5 --- drb-frontend/app/globals.css | 14 +++++++++++++ drb-frontend/tailwind.config.ts | 36 +++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/drb-frontend/app/globals.css b/drb-frontend/app/globals.css index 93a7262..083802f 100644 --- a/drb-frontend/app/globals.css +++ b/drb-frontend/app/globals.css @@ -67,6 +67,10 @@ html, body { font-family: var(--font-mono), ui-monospace, monospace; } +/* Dark mode: raw gray-600/700 text sits ~2.4:1 on the near-black page — below + * readable. Lift both to gray-500 (~4:1). */ +.dark .text-gray-600, .dark .text-gray-700 { color: #6b7280; } + /* ── Light mode overrides ─────────────────────────────────────────────────── */ /* * The app's components use hardcoded dark-palette Tailwind classes (bg-gray-9xx, @@ -84,6 +88,7 @@ html:not(.dark) .bg-gray-900\/60 { background-color: rgba(255,255,255,0.85) ! html:not(.dark) .bg-gray-900\/50 { background-color: rgba(255,255,255,0.75) !important; } html:not(.dark) .bg-gray-900\/30 { background-color: rgba(255,255,255,0.50) !important; } html:not(.dark) .bg-gray-800 { background-color: #f1f5f9 !important; } +html:not(.dark) .bg-gray-800\/60 { background-color: rgba(226,232,240,0.70) !important; } html:not(.dark) .bg-gray-800\/40 { background-color: rgba(241,245,249,0.60) !important; } html:not(.dark) .bg-gray-800\/30 { background-color: rgba(241,245,249,0.50) !important; } html:not(.dark) .bg-gray-700 { background-color: #e2e8f0 !important; } @@ -91,16 +96,25 @@ html:not(.dark) .bg-gray-700 { background-color: #e2e8f0 !important; } /* Borders */ html:not(.dark) .border-gray-800 { border-color: #e2e8f0 !important; } html:not(.dark) .border-gray-700 { border-color: #cbd5e1 !important; } +html:not(.dark) .border-gray-600 { border-color: #94a3b8 !important; } +html:not(.dark) .border-gray-800\/60 { border-color: #e2e8f0 !important; } html:not(.dark) .divide-gray-800 > * + * { border-color: #e2e8f0 !important; } /* Text */ html:not(.dark) .text-white { color: #0f172a !important; } html:not(.dark) .text-gray-100 { color: #1e293b !important; } +html:not(.dark) .text-gray-200 { color: #1e293b !important; } html:not(.dark) .text-gray-300 { color: #334155 !important; } html:not(.dark) .text-gray-400 { color: #475569 !important; } html:not(.dark) .text-gray-500 { color: #64748b !important; } html:not(.dark) .text-gray-600 { color: #94a3b8 !important; } +/* …except on saturated fills (primary/danger/success buttons), where the fill + * stays dark in both themes — remapping to navy made their labels unreadable. */ +html:not(.dark) .text-white:is(.bg-accent, .bg-sev-major, .bg-sev-moderate, + .bg-indigo-500, .bg-indigo-600, .bg-indigo-700, .bg-red-600, .bg-red-700, + .bg-green-600, .bg-green-700, .bg-yellow-700, .bg-yellow-800, .bg-gray-600) { color: #ffffff !important; } + /* Hover states */ html:not(.dark) .hover\:bg-gray-900:hover { background-color: #f8fafc !important; } html:not(.dark) .hover\:bg-gray-900\/50:hover { background-color: rgba(255,255,255,0.75) !important; } diff --git a/drb-frontend/tailwind.config.ts b/drb-frontend/tailwind.config.ts index 1c645a1..62254d9 100644 --- a/drb-frontend/tailwind.config.ts +++ b/drb-frontend/tailwind.config.ts @@ -1,5 +1,8 @@ import type { Config } from "tailwindcss"; +const tok = (name: string) => + `color-mix(in srgb, var(--${name}) calc( * 100%), transparent)`; + const config: Config = { content: [ "./app/**/*.{ts,tsx}", @@ -15,24 +18,27 @@ const config: Config = { // Semantic tokens — defined as CSS custom properties in app/globals.css on // :root (light) and .dark (dark). Components must use these names, never a // raw gray-9xx, so a theme is one variable block rather than an override sheet. + // Wrapped in color-mix so opacity modifiers (bg-surface/90, bg-accent/15) + // work — a bare var() hex gives Tailwind nothing to apply alpha to, and the + // class silently generates no rule (map overlays rendered see-through). colors: { - page: "var(--page)", - surface: "var(--surface)", - raised: "var(--raised)", - line: "var(--line)", - "line-strong": "var(--line-strong)", + page: tok("page"), + surface: tok("surface"), + raised: tok("raised"), + line: tok("line"), + "line-strong": tok("line-strong"), ink: { - DEFAULT: "var(--ink)", - 2: "var(--ink-2)", - muted: "var(--ink-muted)", + DEFAULT: tok("ink"), + 2: tok("ink-2"), + muted: tok("ink-muted"), }, - accent: "var(--accent)", - "sev-moderate": "var(--sev-moderate)", - "sev-major": "var(--sev-major)", - "map-bg": "var(--map-bg)", - "map-block": "var(--map-block)", - "map-road": "var(--map-road)", - "map-water": "var(--map-water)", + accent: tok("accent"), + "sev-moderate": tok("sev-moderate"), + "sev-major": tok("sev-major"), + "map-bg": tok("map-bg"), + "map-block": tok("map-block"), + "map-road": tok("map-road"), + "map-water": tok("map-water"), }, // Marketing/product type scale — used by the (marketing) surface and // settings shell so headings read as a deliberate hierarchy rather than