Automatically Catching Contrast Issues in Pull Requests

Automatically Catching Contrast Issues in Pull Requests
GitHub Actions List

As part of my work on LittleLink, I built an automated contrast checker GitHub Action that runs every time a pull request touches brands.css. It looks for changes to button classes, evaluates the contrast ratios between foreground text and background colors, and ensures buttons meet WCAG minimums—even across both light and dark themes.

Ensuring accessible design isn’t just the right thing to do—it’s a vital part of building inclusive products. I used to think accessibility was something to check off at the end of a project, or embarrassingly enough, not consider it at all. While WCAG guidelines help define what’s accessible, automating that check in LittleLink helps ensure accessibility doesn’t fall through the cracks when a new pull request is opened.

How It Works

This custom workflow hooks into pull requests using a GitHub Action. Here's what it does:

  1. Detects changes in CSS: It inspects git diff to find modified .button-* classes.
  2. Parses color tokens: For each detected button class, it looks at:
    • --button-text
    • --button-background
    • --button-border
  3. Checks contrast ratios using WCAG 2.1 guidelines.
  4. Validates visibility across:
    • Text on button background
    • Button background on light theme (#ffffff)
    • Button background on dark theme (#121212)
  5. Handles gradients by flagging them for manual review, since contrast calculations on dynamic backgrounds can’t be automated reliably.
  6. Considers border strokes: If a low-contrast button includes a high-contrast border (e.g. white on dark), the check treats it as passing.

Sample Output

Here’s how different scenarios look when a PR runs:

✅ Fully Passing

Console output showing a fully passing addition.

A high-contrast button like newButton passes all checks—even across themes. If there's a contrast fail but a high-contrast border is present (e.g. white stroke on a dark theme), it's treated as resolved.


⚠️ Manual Review Required

Console output flagging a manual review for a gradient.

Buttons using background-image (gradients) are flagged and require a manual review to confirm readability.


❌ Failing Contrast

Console output showing a failed contrast check due to not having a stroke for dark theme.

If contrast fails and no corrective border is present, the PR fails. In this example, newButton has a text/background contrast ratio of 1.16, far below the required 4.5. A stroke is recommended to fix it.


The GitHub Action

The action is defined in contrast-check.yml and contains a script that:

Here’s a snippet:

on:
  pull_request:
    paths:
      - 'css/brands.css'

And the shell script:

check_contrast "#ffffff" "$bg_color" "BUTTON vs LIGHT THEME" "$border_color" "#000000"
check_contrast "#121212" "$bg_color" "BUTTON vs DARK THEME" "$border_color" "#ffffff"

You can view the full script in the repo.


Why I Built This

LittleLink has dozens of button styles contributed by the community, and it's easy to introduce a visually appealing style that isn’t readable to everyone. Rather than reviewing them manually, this tool automates the tedium and flags any problems immediately during PR review.

It also doubles as a learning tool—offering real-time feedback and guidance like:

“Contrast ratio 1.16 fails WCAG — Recommend adding a #ffffff stroke.”

Message Board