A11y Contrast Lab is a small Python tool, with no runtime dependency, for calculating WCAG 2.x contrast ratios and auditing:
- a single color pair;
- a CSV file of foreground/background pairs;
- a simple JSON design-token file;
- a JSON or Markdown report usable in design-system review or CI.
The topic is digital accessibility. Insufficient contrast between text and its background can make an interface difficult or impossible to read. This tool does not replace a full accessibility audit: it only measures one important, partial condition for already-composited opaque RGB colors.
- Fixed human
pairoutput: all computed verdicts are displayed, includinglarge text AAA. - Hardened
--outputwrite errors: clean stderr message and non-zero exit code, without a raw Python traceback. - Added minimal packaging with
pyproject.tomland thea11y-contrastconsole entrypoint. - Added a
tokenscommand to audit a simple JSON design-token file. - Added Markdown output for CSV and token reports through
--format markdown. - Extended tests and validation: 23 unit/CLI tests, CSV and token reproduction, minimal WCAG reference validation, and tested editable installation.
Out of scope for v2: full CSS parsing, alpha/compositing, APCA/WCAG 3, real font-size validation, page audits, and browser audits.
The tool implements WCAG 2.x contrast calculation for already-composited opaque RGB colors.
For each sRGB channel encoded between 0 and 255:
c = channel / 255
linear = c / 12.92 if c <= 0.04045
linear = ((c + 0.055) / 1.055) ** 2.4 otherwise
Relative luminance is then:
L = 0.2126 * R + 0.7152 * G + 0.0722 * B
The contrast ratio is:
(L_lighter + 0.05) / (L_darker + 0.05)
Exposed thresholds:
- normal text AA: 4.5:1
- normal text AAA: 7:1
- large text AA: 3:1
- large text AAA: 4.5:1
- non-text AA: 3:1
WCAG references used for the minimal validation:
- https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum
- https://www.w3.org/WAI/GL/wiki/Contrast_ratio
a11y_contrast/
contrast.py calculation library, CSV audit, token audit
cli.py command-line interface
__main__.py python3 -m a11y_contrast support
examples/
sample_pairs.csv CSV example dataset
tokens.json design-token example dataset
reports/
sample_report.json CSV report reproduced by script
sample_report.md CSV Markdown report
cli_report.json CSV report generated by the CLI
aa_gate_report.json CSV report generated with the normal AA gate
tokens_report.json token report reproduced by script
tokens_cli_report.json token report generated by the CLI
tokens_report.md token Markdown report
tokens_aa_gate_report.json token report generated with the normal AA gate
pair_777_on_white.json example near the AA threshold
wcag_reference_validation.json minimal reference validation
benchmark.json deterministic smoke benchmark
scripts/
reproduce_results.py regenerates and verifies the main reports
validate_wcag_reference.py compares a few cases to WCAG facts
benchmark.py small contrast-calculation benchmark
tests/
test_contrast.py unittest automated tests
pyproject.toml minimal packaging and installable entrypoint
DECISION_LOG.md decision log
RESULTS.md results and verification notes
NOTES.md limits, notes, and future work
Run directly from a checkout:
python3 -m a11y_contrast pair '#000' '#fff'Local editable installation:
python3 -m pip install -e .
a11y-contrast pair '#000' '#fff'The package has no external runtime dependency. Editable builds use the
standard setuptools backend.
Evaluate one pair:
python3 -m a11y_contrast pair '#000000' '#ffffff'JSON output for one pair:
python3 -m a11y_contrast pair '#777777' '#ffffff' --jsonAudit a CSV file as JSON:
python3 -m a11y_contrast csv examples/sample_pairs.csv --output reports/cli_report.jsonAudit a CSV file as Markdown:
python3 -m a11y_contrast csv examples/sample_pairs.csv --format markdown --output reports/sample_report.mdAudit JSON design tokens:
python3 -m a11y_contrast tokens examples/tokens.json --output reports/tokens_report.jsonAudit design tokens as Markdown:
python3 -m a11y_contrast tokens examples/tokens.json --format markdown --output reports/tokens_report.mdFail a CI command when an invalid entry exists:
python3 -m a11y_contrast csv examples/sample_pairs.csv --fail-on-invalidFail a CI command when a valid pair fails normal text AA:
python3 -m a11y_contrast tokens examples/tokens.json --fail-on-aa-normalMinimal accepted format:
{
"button.primary": {
"foreground": "#ffffff",
"background": "#005fcc"
},
"button.secondary": {
"foreground": "#777777",
"background": "#ffffff"
}
}The token report includes total tokens, valid tokens, invalid tokens, normal-text AA passes/failures, and token-level details.
0: command completed successfully.2: invalid input, unreadable file, invalid JSON/CSV,--outputwrite error, or a failure requested by--fail-on-invalid.3: at least one valid entry fails normal text AA while--fail-on-aa-normalis enabled.
argparse may also return 2 for command-line usage errors.
python3 -m unittest discover -s tests -v
python3 scripts/reproduce_results.py
python3 scripts/validate_wcag_reference.py
python3 scripts/benchmark.py
python3 -m a11y_contrast csv examples/sample_pairs.csv --output reports/cli_report.json
diff -u reports/sample_report.json reports/cli_report.jsonOn examples/sample_pairs.csv:
- 7 audited rows;
- 6 valid rows;
- 1 detected invalid row;
- 4 valid pairs pass normal text AA;
- 2 valid pairs fail normal text AA;
- minimum ratio: 1.0;
- maximum ratio: 21.0.
On examples/tokens.json:
- 5 tokens;
- 3 valid tokens;
- 2 invalid tokens;
- 2 valid tokens pass normal text AA;
- 1 valid token fails normal text AA.
Representative cases:
#000000on#ffffffgives21.0:1.#767676on#ffffffgives about4.5422:1and passes normal text AA.#777777on#ffffffgives about4.4781:1and fails normal text AA.#12xz00is rejected as an invalid color.
- Only opaque hexadecimal colors are accepted:
#RGB,RGB,#RRGGBB, andRRGGBB. - The tool does not composite alpha and does not parse CSS.
- The result does not account for actual font size, font weight, antialiasing, visual context, or individual perception.
- This is not a full accessibility audit.
- The tool implements the classic WCAG 2.x contrast thresholds, not APCA or a future WCAG 3 model.
- Add a command that suggests a nearby color meeting a chosen threshold.
- Add optional parsers for more realistic design-token formats.
- Add an HTML report readable by non-developers.
- Compare results against a larger external reference corpus.