Contrast belongs to the pair, not the color
Lesson 2 · utune-ai.vercel.app · Measure every text/background pair, then fix only the failing pairs.
The real reason uTune AI is hard to read — in low light or any light — is that its most-used body-text gray, #9aa0aa (97 occurrences), sits on white and cream at roughly 2.5:1 contrast, when readable body text needs 4.5:1. This lesson's win: you can measure any pair in Chrome DevTools, quote the thresholds from memory, and name the two fixes this site needs.
The rule: WCAG AA thresholds
WCAG 2.2 success criterion 1.4.3 (Contrast, Minimum) sets two numbers:
| Text kind | Minimum ratio | On this site |
|---|---|---|
| Body text (under ~24px regular / ~18.5px bold) | 4.5:1 | All the gray copy, nav links, card metadata |
| Large text (≥24px regular or ≥18.5px bold) | 3:1 | The 40–64px hero headline |
Ratios run from 1:1 (identical colors) to 21:1 (black on white). The ratio is computed from relative luminance, so it needs both colors — which is why the same foreground can pass in one place and fail in another.
Measure it in Chrome DevTools
- Right-click the pale “Search › Enrich › Analyze › Close” kicker text → Inspect.
- In the Styles pane, find the
colordeclaration and click its little color swatch. - The color picker shows a Contrast ratio row. Expand it: you get the computed ratio against the rendered background, AA/AAA checkmarks, and two white curves on the color field — drag the color below the top line and it passes AA.
Two power-ups: the Lighthouse accessibility audit lists every failing pair on the page at once, and the Rendering tab → Emulate vision deficiencies → Blurred vision gives you a fast “low-light squint test.”
The measurements for this site
Listen first: four pairs fail. The muted gray 9aa0aa scores about two-point-six to one on white, two-point-five on cream, and two-point-three on light gray — all far below four-point-five. White hero text on the sky gradient scores between one-point-five and two to one. Meanwhile the same 9aa0aa gray scores seven-point-two to one on the dark section background, a comfortable pass — proof that the color isn't broken, the pairing is.
Read that table twice: #9aa0aa fails on cream and passes on the dark mockup cards. Contrast is a property of the pair. So the fix is not “delete this color” — it's “stop pairing it with light backgrounds.”
Fix 1 — darken the muted gray on light backgrounds
Listen first: replace the muted gray 9aa0aa with 646a78 wherever it sits on a light background. That value was chosen by testing it against all six light backgrounds the site uses; its worst score is four-point-five-nine to one, so every pairing passes AA. Occurrences inside the dark mockup cards keep the original gray.
If you'd rather consolidate than add a value: the site's existing #6b7180 passes on white and cream, but only reaches 4.29 on #f0f0f0 — so it can't be the single universal muted tone. #646a78 can, and it keeps the same cool-gray character. (This is also the moment to name each value's role — “muted-on-light” vs “muted-on-dark” — which is the token thinking from Lesson 1.)
Fix 2 — the hero headline over the sky
White at 40–64px is “large text,” so it only needs 3:1 — and it still fails (1.5–2.0:1). Three honest options, in order of least design disruption:
- Use ink instead of white: the site's own
#0f1115scores 9.5:1 on the sky top and 12.9:1 mid-gradient. One class change, guaranteed pass. - Put a scrim behind the text: a soft dark overlay (e.g.
rgba(15,17,21,0.55)panel or a bottom-up gradient) restores white text to a passing background. The existingdrop-shadow(0 1px 2px rgba(0,0,0,0.15))is far too weak to count. - Darken the sky's top stop so white passes: even
#2f7fc4only reaches 4.24:1 (passes 3:1 large-text, fails for the 13.5px white nav links) — so if the nav stays white, it needs its own scrim or pill background regardless.
Note the trap in option 3: the headline and the 13.5px nav links share the sky but not the threshold — the nav needs 4.5:1. Check each pair at its own threshold.
“It looks fine on my screen.” Your calibrated, bright desktop monitor is the best case. Low-light phone use, night-shift mode, cheap panels, and older eyes all push effective contrast down — the 4.5:1 floor exists to keep text readable in those conditions, not the ideal ones.
Contrast is a pair property —
#9aa0aa passes on the dark cards (7.2:1) and fails on cream (2.5:1) — so the fix is to darken it to #646a78 on light backgrounds (AA everywhere: 4.59–5.42) and give the hero headline ink text or a scrim, checking body text at 4.5:1 and large text at 3:1.
Read next
- ← Lesson 1: Diagnose why the page can't go dark (dark mode itself is deferred, but the CSS-archaeology technique is reused here)
- Lesson 3 (planned): apply the fixes in the codebase and verify with Lighthouse — needs access to the uTune AI repo.
Primary source
W3C — Understanding SC 1.4.3: Contrast (Minimum) (~10-min read): the definitive statement of the 4.5:1/3:1 thresholds, what counts as large text, and the exceptions.
💬 I'm your teacher for this — ask me followups any time.
Lesson 2 · workspace utune-ai · mission: make utune-ai.vercel.app easy to read in low light