Skip to content

Sorok-Dva/a11y-contrast-lab

Repository files navigation

A11y Contrast Lab

Version francaise

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.

v2 changes

  • Fixed human pair output: all computed verdicts are displayed, including large text AAA.
  • Hardened --output write errors: clean stderr message and non-zero exit code, without a raw Python traceback.
  • Added minimal packaging with pyproject.toml and the a11y-contrast console entrypoint.
  • Added a tokens command 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.

Method

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:

Project structure

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

Installation

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.

Usage

Evaluate one pair:

python3 -m a11y_contrast pair '#000000' '#ffffff'

JSON output for one pair:

python3 -m a11y_contrast pair '#777777' '#ffffff' --json

Audit a CSV file as JSON:

python3 -m a11y_contrast csv examples/sample_pairs.csv --output reports/cli_report.json

Audit a CSV file as Markdown:

python3 -m a11y_contrast csv examples/sample_pairs.csv --format markdown --output reports/sample_report.md

Audit JSON design tokens:

python3 -m a11y_contrast tokens examples/tokens.json --output reports/tokens_report.json

Audit design tokens as Markdown:

python3 -m a11y_contrast tokens examples/tokens.json --format markdown --output reports/tokens_report.md

Fail a CI command when an invalid entry exists:

python3 -m a11y_contrast csv examples/sample_pairs.csv --fail-on-invalid

Fail a CI command when a valid pair fails normal text AA:

python3 -m a11y_contrast tokens examples/tokens.json --fail-on-aa-normal

JSON token format

Minimal 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.

Exit codes

  • 0: command completed successfully.
  • 2: invalid input, unreadable file, invalid JSON/CSV, --output write error, or a failure requested by --fail-on-invalid.
  • 3: at least one valid entry fails normal text AA while --fail-on-aa-normal is enabled.

argparse may also return 2 for command-line usage errors.

Reproduce the results

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.json

Current results

On 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:

  • #000000 on #ffffff gives 21.0:1.
  • #767676 on #ffffff gives about 4.5422:1 and passes normal text AA.
  • #777777 on #ffffff gives about 4.4781:1 and fails normal text AA.
  • #12xz00 is rejected as an invalid color.

Limits

  • Only opaque hexadecimal colors are accepted: #RGB, RGB, #RRGGBB, and RRGGBB.
  • 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.

Next steps

  • 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.

About

A small local Python tool for checking WCAG 2.x contrast ratios for color pairs, CSV files, and JSON design tokens, with a CLI, Markdown/JSON reports, and reproducible tests.

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages