v1.7.0 — dark-aware gradient text + bg-clip-text contrast gate
Lockstep npm MINOR 1.6.0 → 1.7.0 (@willink-labs/tokens · tailwind-preset · react · css-tokens). No Flutter release (willink_theme stays 1.5.0, ADR-0011).
Decisions: ADR-0018 · cycle v1.8 roadmap. PR #61.
What shipped
Dark-aware text-gradient-primary (tailwind-preset, real content). The bg-clip-text gradient heading painted its glyphs with the fixed brand → brand-glow pair (brand-600 → brand-500), which does not flip (ADR-0013) — so on the dark bg the worst endpoint rendered at 3.54:1, below AA, washing out. New preset-internal --color-gradient-primary-from/-to make the endpoints dark-aware: light byte-identical (brand → brand-glow, worst 4.23:1), dark brand-300 → brand-400 (worst 7.41:1), staying recognizably brand-purple. bg-gradient-primary / bg-gradient-ai unchanged (vivid bg behind white text).
Closed the audit blind spot the CEO caught twice. scripts/check-contrast.mjs gained a TEXT_GRADIENTS registry that checks every text-clipped gradient's worst endpoint against bg — required ≥ 4.5 in dark, report-only baseline in light — wired into CI via the existing tokens contrast gate. A deliberately-bad endpoint (the pre-1.7 fixed pair) turns the gate red (3.54:1 ✗, exit 1). bg-clip-text gradient headings are now first-class contrast-gate citizens.
Consumer contract for accent-on-gradient text. New docs/a11y/gradient-and-accent.md documents the rule the DS can't enforce (hue/lightness separation for custom accent text on gradient backgrounds — the i-willink.com hero blind spot).
tokens / css-tokens / react carry lockstep markers (the gradient vars are preset-internal — not semantic.json / css-tokens output / component refs, mirroring --color-gradient-subtle-end). reduced-motion behavior unaffected.
🤖 Generated with Claude Code