From 969d554b5b0b18f8a70d8cc985fcd85160e92ead Mon Sep 17 00:00:00 2001 From: Krish Date: Sun, 23 Aug 2026 23:28:42 +0530 Subject: [PATCH] style: apply the EchoAssist brand palette Swap the dark theme's color values for the brand palette, hoisted into :root tokens. Structure, layout, and spacing are untouched. Deep Navy #243B53 page background Soft Echo Blue #6F9FD8 buttons, active states Signal Cream #F4E9B8 category badges Mist Blue #DCEAF6 card surfaces (8% over navy), light text White primary text Three places where a literal swap would have hurt readability, measured against WCAG AA on the rendered pixels rather than on the token values: - Button text is deep navy, not white. White on Soft Echo Blue is 2.75:1; navy on it is 5.5:1. The accent fill is the one surface white cannot sit on. - Links use a lightened Echo Blue (#8fb8e6). The brand blue on navy is 4.18:1, just under AA for 13px nav text; the tint holds the hue at 5.6:1. - Added an ::placeholder rule. The UA default was 2.97:1, and the locked question's "Locked - time expired." message is a placeholder, so it was real UI text failing contrast. Also raises .question.locked from 0.65 to 0.9. The dim is what cost that card its contrast, and no color value can reach it since opacity scales text and background together. At 0.9 everything in the locked card passes except the "time up" label at 4.34. Audited every text node in the quiz flow in a headless browser, compositing the translucent card and inherited opacity. Old theme: 7 AA failures. New theme: 1. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PQgFu8aJXDBmpetgSpsGut --- frontend/src/styles/globals.css | 88 +++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/frontend/src/styles/globals.css b/frontend/src/styles/globals.css index 173497d..63eea7d 100644 --- a/frontend/src/styles/globals.css +++ b/frontend/src/styles/globals.css @@ -1,65 +1,89 @@ +/* EchoAssist brand palette + Deep Navy #243B53 · White · Soft Echo Blue #6F9FD8 · Signal Cream #F4E9B8 · Mist Blue #DCEAF6 */ +:root { + --navy: #243B53; /* Deep Navy — page background */ + --navy-sunken: #1b2f44; /* inputs, inset surfaces */ + --surface: rgba(220, 234, 246, 0.08); /* Mist Blue at low opacity — cards */ + --border: #3c5c7a; /* subtle Mist-tinted border */ + + --accent: #6F9FD8; /* Soft Echo Blue — buttons, active states */ + --accent-text: #8fb8e6; /* lightened Echo Blue for link text on navy */ + --on-accent: #16283a; /* text on an Echo Blue fill */ + --cream: #F4E9B8; /* Signal Cream — badges, highlights */ + --mist: #DCEAF6; /* Mist Blue — light text/borders */ + + --text: #ffffff; + --text-muted: #a9c3dc; + --text-subtle: #8fadc9; + --danger: #ffa3a3; + --success: #5bd68c; +} + * { box-sizing: border-box; } body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; - background: #0f1115; - color: #e8e8ea; + background: var(--navy); + color: var(--text); } .container { max-width: 720px; margin: 0 auto; padding: 48px 20px; } h1 { margin-bottom: 4px; } -.tagline { color: #9a9aa5; margin-top: 0; } +.tagline { color: var(--text-muted); margin-top: 0; } .row { display: flex; gap: 10px; margin: 24px 0; } input, textarea { width: 100%; padding: 10px 12px; border-radius: 8px; - border: 1px solid #2a2d36; - background: #171922; - color: #e8e8ea; + border: 1px solid var(--border); + background: var(--navy-sunken); + color: var(--text); font-size: 14px; } textarea { min-height: 80px; margin-top: 8px; resize: vertical; } +/* The UA default placeholder is ~3:1 on this navy, and the locked-question + message ("Locked — time expired.") is a placeholder. */ +input::placeholder, textarea::placeholder { color: var(--text-subtle); opacity: 1; } button { padding: 10px 18px; border-radius: 8px; border: none; - background: #5b8cff; - color: white; + background: var(--accent); + color: var(--on-accent); font-weight: 600; cursor: pointer; white-space: nowrap; } button:disabled { opacity: 0.5; cursor: not-allowed; } -.question { margin-bottom: 20px; padding: 16px; background: #171922; border-radius: 10px; } -.ref { color: #7d8194; font-size: 13px; } -.error { color: #ff6b6b; } +.question { margin-bottom: 20px; padding: 16px; background: var(--surface); border-radius: 10px; } +.ref { color: var(--text-muted); font-size: 13px; } +.error { color: var(--danger); } .result { margin-top: 24px; } -.feedback-item { padding: 12px 16px; background: #171922; border-radius: 10px; margin-bottom: 10px; } -.feedback-item .q { margin: 0 0 4px 0; color: #c7c9d1; } -.feedback-item .s { margin: 0; color: #9a9aa5; font-size: 14px; } +.feedback-item { padding: 12px 16px; background: var(--surface); border-radius: 10px; margin-bottom: 10px; } +.feedback-item .q { margin: 0 0 4px 0; color: var(--mist); } +.feedback-item .s { margin: 0; color: var(--text-muted); font-size: 14px; } .result .headline { margin: 0 0 4px 0; } -.result .complexity { color: #9a9aa5; font-weight: 500; } -.result .complexity-why { margin: 0 0 16px 0; color: #7d8194; font-size: 13px; } +.result .complexity { color: var(--text-muted); font-weight: 500; } +.result .complexity-why { margin: 0 0 16px 0; color: var(--text-subtle); font-size: 13px; } -.rules { color: #7d8194; font-size: 13px; margin: 0 0 16px 0; } +.rules { color: var(--text-subtle); font-size: 13px; margin: 0 0 16px 0; } .question .meta { display: flex; align-items: center; gap: 10px; margin: 6px 0 0 0; } .question .cat { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; - color: #7d8194; border: 1px solid #2a2d36; border-radius: 999px; padding: 2px 8px; + color: var(--cream); border: 1px solid rgba(244, 233, 184, 0.35); border-radius: 999px; padding: 2px 8px; } .question .timer { margin-left: auto; font-variant-numeric: tabular-nums; - font-size: 13px; color: #9a9aa5; + font-size: 13px; color: var(--text-muted); } -.question .timer.urgent { color: #ff6b6b; font-weight: 600; } -.question.locked { opacity: 0.65; } +.question .timer.urgent { color: var(--danger); font-weight: 600; } +.question.locked { opacity: 0.9; } .question textarea:disabled { cursor: not-allowed; } .topbar { @@ -67,26 +91,26 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } display: flex; justify-content: flex-end; gap: 18px; } .linkish { - background: none; border: none; color: #5b8cff; + background: none; border: none; color: var(--accent-text); padding: 0; font-weight: 600; cursor: pointer; text-decoration: underline; } .auth { display: flex; flex-direction: column; gap: 14px; max-width: 360px; } .auth h2 { margin: 0; } -.auth label { display: flex; flex-direction: column; gap: 6px; font-size: 14px; color: #9a9aa5; } +.auth label { display: flex; flex-direction: column; gap: 6px; font-size: 14px; color: var(--text-muted); } .auth select { - padding: 10px 12px; border-radius: 8px; border: 1px solid #2a2d36; - background: #171922; color: #e8e8ea; font-size: 14px; + padding: 10px 12px; border-radius: 8px; border: 1px solid var(--border); + background: var(--navy-sunken); color: var(--text); font-size: 14px; } -.auth .switch { margin: 0; font-size: 14px; color: #9a9aa5; } -.auth .hint { font-size: 12px; color: #7d8194; } +.auth .switch { margin: 0; font-size: 14px; color: var(--text-muted); } +.auth .hint { font-size: 12px; color: var(--text-subtle); } -.linkish.current { color: #e8e8ea; text-decoration: none; } +.linkish.current { color: var(--text); text-decoration: none; } -.profile-card { margin-top: 24px; padding: 16px; background: #171922; border-radius: 10px; } -.profile-card .s { margin: 0 0 8px 0; color: #9a9aa5; font-size: 14px; } +.profile-card { margin-top: 24px; padding: 16px; background: var(--surface); border-radius: 10px; } +.profile-card .s { margin: 0 0 8px 0; color: var(--text-muted); font-size: 14px; } .profile-card .s:last-child { margin-bottom: 0; } .reveal-state { margin: 0 0 10px 0; font-size: 11px; text-transform: uppercase; - letter-spacing: 0.06em; color: #7d8194; + letter-spacing: 0.06em; color: var(--text-muted); } -.reveal-state.revealed { color: #5bd68c; } +.reveal-state.revealed { color: var(--success); }