Skip to content

fix(blocks): nav CTA legibility — on-primary text wins the cascade - #70

Merged
nandal merged 1 commit into
devfrom
fix/nav-cta-contrast
Jul 22, 2026
Merged

fix(blocks): nav CTA legibility — on-primary text wins the cascade#70
nandal merged 1 commit into
devfrom
fix/nav-cta-contrast

Conversation

@bytesbrains

Copy link
Copy Markdown
Owner

Fixes #69

What

The nav's highlighted CTA button rendered its label in var(--muted) instead of var(--on-primary) — near-invisible text on the primary fill across all 6 presets and 133/160 starter templates (113 below 3:1).

1. Cascade fix — src/blocks/nav.ts

The CTA anchor lives inside <div class="links">, and .blk-nav .links a (specificity 0-2-1) out-ranked .blk-nav .cta (0-2-0). The generic link rules now exclude the CTA:

.blk-nav .links a:not(.cta){color:var(--muted);…}
.blk-nav .links a:not(.cta):hover{color:var(--text)}
.blk-nav .cta{…;color:var(--on-primary);transition:filter var(--motion)}
.blk-nav .cta:hover{filter:brightness(1.08)}

:not(.cta) both excludes and out-specifies, so the CTA keeps its on-fill colour in every state. The CTA also gains a proper hover affordance (brightness) replacing the accidental hover recolour — which on the mono preset used to turn the label #111 on a #111 fill (1.00:1, fully invisible).

2. readableOn crossover fix — src/tokens.ts

The black/white pick flipped at luminance 0.45, but the true contrast crossover is ~0.19 — mid-tone fills got white text below AA where near-black passes. It now returns whichever side clears the higher WCAG ratio, which is optimal by construction.

Note: this changes --on-primary/--on-accent from white to near-black on mid-tone fills (midnight #4c8dff: 3.20:1 → 5.90:1; candy #e0468b: 3.88:1 → 4.87:1) for all on-fill consumers (hero, pricing, forms, …) — an intentional, contrast-improving visual change on those palettes.

Measured result (CTA text on primary)

Preset Before After
sand 1.32:1 🔴 7.73:1 ✅
midnight 1.04:1 🔴 5.90:1 ✅
forest 1.06:1 🔴 5.99:1 ✅
mono 3.29:1 🟠 (hover 1.00:1) 18.88:1 ✅
candy 1.29:1 🔴 4.87:1 ✅
ocean 1.05:1 🔴 5.77:1 ✅

All 133 affected templates inherit the fix (shared block CSS — no per-template changes needed).

Tests

  • render.test.ts: renders nav + CTA and asserts no emitted .blk-nav rule recolours the CTA from a generic .links a selector, and that .cta sets color:var(--on-primary) — the cascade bug cannot silently return.
  • roadmap.test.ts: readableOn mid-tone cases + an RGB-grid sweep asserting the picked side always beats-or-matches the rejected one and clears 4.5:1 wherever either choice can.

npm test: 180/180 pass.

🤖 Generated with Claude Code

Two contrast fixes:

- nav: `.blk-nav .links a` (0,2,1) out-specified `.blk-nav .cta` (0,2,0),
  so the CTA rendered muted-on-primary — 1.32:1 on the default palette,
  failing WCAG AA on all 6 presets and 133/160 starter templates. The
  generic link rules now exclude the CTA via `:not(.cta)`, and the CTA
  gains its own hover affordance (brightness) to replace the accidental
  hover recolour.

- tokens: `readableOn` flipped black/white at luminance 0.45, but the
  contrast crossover is ~0.19 — mid-tone fills got white text below AA
  (e.g. #4c8dff: 3.2:1) where near-black passes (5.7:1). It now returns
  whichever side clears the higher WCAG ratio.

Regression tests: emitted nav CSS may not recolour the CTA from a
generic link rule; readableOn is swept over an RGB grid asserting the
picked side always beats the rejected one and clears 4.5:1 wherever
either can.

Fixes #69

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@wrokin

wrokin Bot commented Jul 22, 2026

Copy link
Copy Markdown

🤖 wrokin code review

Fixes nav CTA text color specificity (issue #69) and improves text contrast selection for mid-tone fills by using explicit ratio comparison instead of luminance threshold.

No high-signal issues — the changes are correct and well tested.

Model: deepseek/deepseek-v4-pro · your key, your model (BYOK)

@wrokin

wrokin Bot commented Jul 22, 2026

Copy link
Copy Markdown

🤖 wrokin security audit

No security-relevant findings in this diff.

Model: deepseek/deepseek-v4-pro · your key, your model (BYOK)

@nandal
nandal merged commit 0a38aca into dev Jul 22, 2026
5 checks passed
@nandal
nandal deleted the fix/nav-cta-contrast branch July 22, 2026 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nav CTA text renders in --muted instead of --on-primary — unreadable on all 6 presets and 133/160 templates (WCAG AA fail)

2 participants