google-labs-code / google-labs-code/design.md
Feature: color-blind contrast lint rule (opt-in, Brettel-Viénot-Mollon simulation)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 28k
- Forks
- 2.3k
- PR merge metrics
- No merged PRs in 30d
Description
Summary
Proposing an opt-in lint rule color-blind-contrast that simulates protanopia, deuteranopia, and tritanopia via standard deficiency matrices and re-runs the contrast check on the simulated colors. Catches palettes where the only cue is a color shift invisible to ~5–10% of users with congenital color-vision deficiency (CVD).
Why this matters
Baseline contrast-ratio (WCAG AA 4.5:1) checks contrast as perceived by full-color vision. A component that passes AA for a tritanopic user can still fail at 2–3:1 for a protanopic user if the palette's primary accent lives in the red-green axis, because protanopia compresses that range.
Real example from our own pilot project:
- Brand orange
#E44001on white#FFFFFF: contrast 4.17:1 (baseline flags this as AA-near-miss) - Same pair under protanopia simulation (
#9D9C10on#FFFFFF): contrast 2.92:1 — below even the 3:1 large-text floor - Same pair under deuteranopia simulation (
#A7B314on#FFFFFF): contrast 2.31:1 — effectively invisible
The author only sees the baseline 4.17:1 warning and may rationalize it as AA-Large-only, not realizing the component is unusable for ~5% of their male audience in any size.
Proposed rule
color-blind-contrast (warning, opt-in)
For each component with a backgroundColor/textColor pair, apply each deficiency matrix to both colors, recompute contrast on the simulated pair, warn if ratio < 3:1.
warning [color-blind-contrast]: component 'button-primary' contrast 2.92:1
under protanopia simulation (bg #9d9c10 on fg #ffffff) is below 3:1 floor
— relying on color-only cues fails this audience
Simulation matrices
Published Brettel-Viénot-Mollon approximations. Applied to normalized sRGB [0,1]:
const MATRICES = {
protanopia: [[0.567, 0.433, 0.0], [0.558, 0.442, 0.0], [0.0, 0.242, 0.758]],
deuteranopia: [[0.625, 0.375, 0.0], [0.7, 0.3, 0.0], [0.0, 0.3, 0.7]],
tritanopia: [[0.95, 0.05, 0.0], [0.0, 0.433, 0.567], [0.0, 0.475, 0.525]],
};
These are the simplified matrices commonly cited in a11y tooling (e.g. Stark, Figma's CVD plugin). Sufficient for flagging high-risk palettes, not a substitute for live testing with CVD users.
Why opt-in
CVD simulation is a stricter bar than baseline WCAG AA. Projects that have explicitly prioritized it should declare intent:
x-oknemix-a11y-target: AAA
# OR a dedicated toggle
lint-rules:
color-blind-contrast: warning
Opt-in avoids noise for projects that haven't made the decision yet, while giving a clear upgrade path.
Implementation notes
Working in our @google/design.md bridge today:
a11y.ts— 3 matrices + contrast re-check, ~40 lines for the CB rule specificallytest-a11y.ts24/24 passing (CB rule is 4 of those tests)- Currently flagging real accessibility regressions on consumer products
Happy to PR as an opt-in rule in DEFAULT_RULES with a sensible default severity, or as an exportable rule for runLinter({ rules: [...custom, colorBlindContrast] }).
Prior art / reading
- Brettel, Viénot & Mollon (1997): https://www.researchgate.net/publication/14397480
- Color Oracle: https://colororacle.org/
- WCAG 2.2 Guideline 1.4.1 Use of Color: https://www.w3.org/WAI/WCAG22/Understanding/use-of-color
- Stark CVD simulation: https://www.getstark.co/features/color-blind-simulator/
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading a11y.ts and running the existing test-a11y.ts suite, which the issue says includes four color-blind-contrast tests. Check how DEFAULT_RULES and runLinter({ rules: [...] }) expose rules, then confirm the opt-in configuration, three simulations, contrast warnings, and regression coverage are all represented when the feature is complete.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- accessibility, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100