Measure the accessibility rules instead of only writing them down - #3
Draft
aievolutionpl wants to merge 1 commit into
Draft
aievolutionpl wants to merge 1 commit into
aievolutionpl wants to merge 1 commit into
Conversation
v2.0.0 documented the contrast and focus requirements. Nothing checked them, so this repository was violating its own 🔴 BLOCKERS in 14 of 15 styles. Adding the check, then fixing everything it found. New: scripts/check_contrast.py Measures every style palette against WCAG AA — text pairs at 4.5:1, focus indicators at 3:1, both themes. Two details decide whether it finds anything: - Dark palettes are resolved light-then-dark. Dark blocks are partial overrides, so reading the dark block alone skips exactly the pairs most likely to fail. - Translucent colors are composited before measuring. A ring authored as a 5.2:1 blue at 35% alpha paints at 1.8:1, and only the second number matters. Waivers exist but require a written reason; there are currently none. Fixed: focus rings were invisible in 14 of 15 styles Every style shipped a translucent focus color (alpha 0.30-0.45), compositing to 1.2-1.8:1 against its own background. All are now solid. Two were also contradicting their own documented §13: 05 specifies a "thick black outline" and shipped pink at 1.98:1; 06 specifies a "high-contrast outline" and shipped neon yellow at 1.04:1. Both now use their text color, as their DNA already said. Fixed: 14 of 15 example pages had no authored focus style at all No :focus-visible, no :focus, no outline anywhere. The browser default is not what the repo asks for. Each page now ships one, using a palette color verified at 3:1 against that page's own background. Fixed: 11 label/fill pairs below 4.5:1 White-on-mid-tone button labels at 1.86-4.48:1. Where the fill is a light tint no light label can pass, so the label went dark; elsewhere the fill was darkened imperceptibly and the label kept. Brand hues are preserved either way — the fill is the brand, the label is chrome. Fixed: Glassmorphism's accent was the hex ANTI-SLOP names --color-accent was #8B5CF6, the exact value ANTI-SLOP.md calls out as the over-used default violet, and it failed contrast at 4.23:1. Now #7C3AED: still unmistakably violet, off the flagged default, 5.70:1. Fixed: gen_tokens.py absorbed misplaced tokens silently It treated every :root as the light palette, including one nested inside @media (prefers-reduced-motion: reduce). A token misplaced there looked correct in the generated JSON while applying only to reduced-motion users in a browser. I hit this myself mid-change. At-rule blocks are now excluded from parsing. CI now enforces both checks The contrast check runs on every push, and a new job renders all 15 example pages at 1440/768/390/360, failing on mobile overflow and uploading the screenshots. The render script shipped in 2.0.0 but only ever ran by hand. Deliberately not checked: --color-border at 3:1. WCAG 1.4.11 requires that of boundaries needed to identify a component, not of every hairline; dividers and subtle card edges are exempt. Holding all 15 styles to it would fail them all and get the check switched off, which costs more than it buys. --shadow-focus carries that requirement instead, and it is checked. Reported as advisory under --verbose. Verified: 226 token pairs pass, 945 structural checks pass, all 15 examples render clean at every viewport. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MSG5kjLk3ZvmZy2C4B17kP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #1 and #2, both merged.
v2.0.0 wrote the contrast and focus rules down. Nothing checked them — so this repository was violating its own 🔴 BLOCKERS in 14 of 15 styles. This adds the check, then fixes everything it found.
70 files changed · +590 / −114The new check
scripts/check_contrast.pymeasures every style palette against WCAG AA: text pairs at 4.5:1, focus indicators at 3:1, in both themes. Two details decide whether it finds anything at all:Waivers exist but require a written reason. There are currently none — everything was fixable.
What it found
🔴 Focus rings were invisible in 14 of 15 styles
Every style shipped a translucent focus color (alpha 0.30–0.45), compositing to 1.2–1.8:1 against its own background — the exact blocker
ANTI-SLOP.mddefines. All are now solid.Two were also contradicting their own documented §13:
05-neo-brutalism#FF90E8at 1.98:1var(--color-text)06-brutalist-anti-grid#CCFF00at 1.04:1var(--color-text)🔴 14 of 15 example pages had no authored focus style at all
No
:focus-visible, no:focus, nooutlineanywhere. The browser default is not what the repo asks for. Each page now ships one, using a palette color verified at 3:1 against that page's own background (4.1:1–18.5:1).11 label/fill pairs below 4.5:1
White-on-mid-tone button labels at 1.86–4.48:1. Where the fill is a light tint no light label can pass, so the label went dark; elsewhere the fill was darkened imperceptibly and the label kept. Brand hues are preserved either way — the fill is the brand, the label is chrome.
Glassmorphism's accent was the hex ANTI-SLOP names
--color-accentwas#8B5CF6— the exact valueANTI-SLOP.md:53calls out as the over-used default violet — and it failed contrast at 4.23:1. Two contradictions, one fix:#7C3AED. Still unmistakably violet, off the flagged default, 5.70:1.gen_tokens.pyabsorbed misplaced tokens silentlyIt treated every
:rootas the light palette, including one nested inside@media (prefers-reduced-motion: reduce). A token misplaced there looked correct in the generated JSON while applying only to reduced-motion users in a browser. I hit this myself mid-change — my own fix landed in the wrong block and the generator made it look right. At-rule blocks are now excluded from parsing.CI now enforces both checks
The contrast check runs on every push, and a new job renders all 15 example pages at 1440/768/390/360, failing on mobile overflow and uploading the screenshots as an artifact.
screenshot.mjsshipped in #2 but only ever ran by hand — a gate nobody runs is not a gate.One thing deliberately not checked
--color-borderat 3:1. WCAG 1.4.11 requires that of boundaries needed to identify a component, not of every hairline — dividers and subtle card edges are exempt, and nearly every design system uses a ~1.2:1 rule for them. My first draft of the checker did flag them, failed all 15 styles, and would have gotten itself switched off.--shadow-focuscarries that requirement instead, and it is checked. Hairlines are reported as advisory under--verbose.Verification
I also opened the renders rather than trusting the exit code: the styles still read as themselves — Glassmorphism is still a vivid gradient field with one frosted moment, Neo-Brutalism still snaps.
Two things I did not act on
Both are judgment calls that belong to you, not to me:
--color-bg, so it can't see this. Catching it needs rendered-pixel sampling — worth doing, bigger feature.ANTI-SLOP.mdforbids invented statistics — but these are clearly demo pages for invented brands, which is arguably a different category. Rewriting 15 pages' copy is a call about what example pages are for.🤖 Generated with Claude Code
https://claude.ai/code/session_01MSG5kjLk3ZvmZy2C4B17kP
Generated by Claude Code