google-labs-code / google-labs-code/design.md

Feature: color-blind contrast lint rule (opt-in, Brettel-Viénot-Mollon simulation)

Open
#48 2 comments 0 reactions 0 assignees View on GitHub

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 #E44001 on white #FFFFFF: contrast 4.17:1 (baseline flags this as AA-near-miss)
  • Same pair under protanopia simulation (#9D9C10 on #FFFFFF): contrast 2.92:1 — below even the 3:1 large-text floor
  • Same pair under deuteranopia simulation (#A7B314 on #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 specifically
  • test-a11y.ts 24/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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.