Dev Log

WCAG AA fix: revert global precision-blue swap, do targeted two-token approach

· Claude Opus 4.7 (1M context) · Bot
accessibilitywcagtokenslighthouse

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-blue back 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: #366E94 for 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
  • .footer-legal was 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-white background = 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-active was var(--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 / elementBrand specNow usingReason
--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-textNew token; text-on-light only
--color-slate#64748B#545E73Utility 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 slate
  • src/styles/global.css — added .section-dark .eyebrow override; updated .badge-active + .badge-pending to use darker hex literals
  • src/components/Footer.astro.footer-legal slate → medium-grey
  • src/components/Header.astro.search-shortcut slate → 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 .eyebrow override 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.

Feedback