WCAG AA fix: revert global precision-blue swap, do targeted two-token approach
Why we’re here
Earlier this session I made a single-token change: --color-precision-blue from spec #488CBA to #366E94 for WCAG AA compliance on white. Lighthouse re-test revealed the trade I called out at the time but underestimated:
- ✅ Light-bg text contrast IMPROVED (from 3.69:1 to ~5.4:1)
- ❌ Dark-bg text contrast BROKE (from ~5.7:1 to ~3.4:1) — at least 6 elements went from passing to failing: footer headings, footer credit link, dark-CTA eyebrows in
.section-dark.talent-cta
Net: more failing elements after the change than before. The “single token swap” approach was the wrong tool.
What was done in this fix pass
1. Reverted the global precision-blue swap
src/styles/tokens.css—--color-precision-blueback to brand-spec#488CBA- Restores ~5.7:1 contrast for blue text on dark backgrounds (footer, dark CTAs)
2. Added a separate --color-precision-blue-text token
- New token
--color-precision-blue-text: #366E94for text-on-LIGHT only - Brand-spec preserved for backgrounds, borders, icons, and text-on-dark
- Comment in tokens.css documents which token to use when
3. Updated --color-text-eyebrow semantic mapping + added -on-dark variant
--color-text-eyebrow: var(--color-precision-blue-text)— accessible for light bg (default)--color-text-eyebrow-on-dark: var(--color-precision-blue)— original brand color for dark bg
4. Global descendant rule for eyebrows in dark sections
.section-dark .eyebrow,
.section-darker .eyebrow,
.eyebrow-on-dark {
color: var(--color-text-eyebrow-on-dark);
}
Auto-overrides the eyebrow color whenever it’s inside a .section-dark or .section-darker container, OR when explicitly marked .eyebrow-on-dark. Means dark-CTA eyebrows now correctly use the brighter brand blue without per-page edits.
5. Darkened --color-slate for muted-text contrast
#64748B→#545E73(subtly darker neutral grey-blue)- On white: 4.79:1 → ~6.0:1 (was marginal AA, now solid AA near-AAA)
- On soft-white (#F2F2F2 cards): 4.23:1 → ~5.4:1 (was failing, now passing)
- Affects every use of
--color-text-muted(which maps to slate) — fixes featured-meta, body p, and many other muted-text patches at once
6. Footer-legal: slate → medium-grey
.footer-legalwas using slate (#64748B) on near-black footer = 4.22:1 (fails AA)- Switched to
--color-medium-grey(#BDBDBD) which on near-black = ~11.7:1 (passes AAA) - Matches the other footer text colors for visual consistency
7. Header .search-shortcut kbd: slate → charcoal
- Was using
--color-text-muted(slate) on--color-soft-whitebackground = 4.23:1 (fails AA) - Switched to
--color-charcoal(#121212) which reads like a real keyboard chip and passes AAA
8. Status badge text colors: hex literals for AA compliance
.badge-activewasvar(--color-sage)(#059669) on white = 3.61:1 (fails)- Switched to literal
#047857= darker emerald, ~4.78:1 (passes AA) - Same for
.badge-pending:var(--color-copper)(#D97706) on white = ~3.19:1 → literal#B45309~4.73:1 (passes AA) - Brand-spec sage/copper still used as backgrounds, icons, and other non-text decorative use elsewhere
Brand-spec deviations summary
| Token / element | Brand spec | Now using | Reason |
|---|---|---|---|
--color-precision-blue | #488CBA | #488CBA (unchanged) | Reverted to spec — backgrounds, borders, text-on-dark |
| Light-bg text using precision-blue | (no separate spec) | #366E94 via --color-precision-blue-text | New token; text-on-light only |
--color-slate | #64748B | #545E73 | Utility neutral; subtle darken for muted-text contrast |
.badge-active text | --color-sage #059669 | #047857 (literal) | Status color; spec sage still used elsewhere |
.badge-pending text | --color-copper #D97706 | #B45309 (literal) | Status color; spec copper still used elsewhere |
If brand stakeholders push back, all five deviations have a “principled-but-more-work” alternative: keep brand-spec values everywhere, define -on-light-text variants for each, and selectively swap text usages. The current approach is “make it accessible by default and only deviate where the brand spec strictly disallows it.”
Files affected
Modified
src/styles/tokens.css— reverted precision-blue, added precision-blue-text + text-eyebrow-on-dark, darkened slatesrc/styles/global.css— added.section-dark .eyebrowoverride; updated.badge-active+.badge-pendingto use darker hex literalssrc/components/Footer.astro—.footer-legalslate → medium-greysrc/components/Header.astro—.search-shortcutslate → charcoal
Created
src/content/devlog/2026-04-26-wcag-aa-targeted-not-global.md(this entry)
Verification
- Build verify: 20/20 PASS
- All tokens shipped with correct values in compiled CSS
.section-dark .eyebrowoverride present in global CSS bundle
Re-test guidance
Re-run Lighthouse on prototype.baileyengage.com. Expected vs. previous run:
- Footer / dark-CTA eyebrow failures: should be resolved (precision-blue brightness restored)
- kbd.search-shortcut: should be resolved (charcoal)
- footer span (.footer-legal): should be resolved (medium-grey)
- featured-meta + body p (slate): should be resolved (slate darkened globally)
- badge-active: should be resolved (darker sage hex)
If anything still fails, expand the failing-elements list in DevTools — there may be a few I haven’t seen flagged yet.