Tutorial: Integrating Accessibility Checks into Your CI/CD Pipeline
7/27/2025
Tutorial: Integrating Accessibility Checks into Your CI/CD Pipeline
Manually checking color contrast is crucial during design and development, but automating these checks in your Continuous Integration/Continuous Deployment (CI/CD) pipeline provides a safety net, preventing inaccessible color combinations from reaching production.
This tutorial explores two ways to integrate automated contrast checks using @weable/a11y-color-utils into a typical CI/CD workflow, focusing on GitHub Actions as an example.
Prerequisites
- A project using Node.js and npm/yarn.
- Basic understanding of CI/CD concepts and GitHub Actions.
- Familiarity with a testing framework like Jest or Vitest (for Option 1).
- The API server from the previous tutorial running (accessible to the CI runner) or deployed (for Option 2).
Why Automate Accessibility Checks?
- Consistency: Ensures standards are checked on every commit/PR.
- Prevention: Catches regressions or newly introduced issues early.
- Efficiency: Frees up developers from repetitive manual checks.
- Compliance: Helps maintain adherence to WCAG standards required by law.
Option 1: Direct Library Integration in Tests (Jest/Vitest)
This approach involves writing unit or integration tests that use @weable/a11y-color-utils directly to verify critical color combinations within your application, often defined in theme files or design system constants.
1. Install Dependencies:
npm install --save-dev jest @weable/a11y-color-utils
# or
npm install --save-dev vitest @weable/a11y-color-utils
2. Create Test File (e.g., tests/accessibility.test.js):
// tests/accessibility.test.js
import {
getColorContrast,
getColorContrastAPCA,
hexToRgb,
} from '@weable/a11y-color-utils';
// --- Configuration ---
// Ideally, import these from your theme/design system constants
const themeColors = {
primaryText: '#212121',
primaryBackground: '#FFFFFF',
accent: '#3498DB',
accentText: '#FFFFFF',
error: '#C0392B',
errorText: '#FFFFFF',
subtleText: '#777777',
// ... add other critical colors
};
const WCAG_AA_NORMAL_THRESHOLD = 4.5;
// Example APCA Threshold (Body Text: 16px/400 weight)
const APCA_BODY_THRESHOLD = 75;
// --- Helper Function ---
function checkContrastPair(label, colorHex1, colorHex2) {
const color1 = hexToRgb(colorHex1);
const color2 = hexToRgb(colorHex2);
if (!color1 || !color2) {
throw new Error(`Invalid HEX color in pair: ${label}`);
}
const wcagRatio = getColorContrast(color1, color2);
const apcaLc = Math.abs(getColorContrastAPCA(color1, color2)); // Use absolute value
// WCAG Check (Minimum AA)
expect(wcagRatio).toBeGreaterThanOrEqual(WCAG_AA_NORMAL_THRESHOLD);
// Optional: APCA Check (Example: Body text readability)
// Note: Adjust threshold based on expected usage (font size/weight)
// expect(apcaLc).toBeGreaterThanOrEqual(APCA_BODY_THRESHOLD);
console.log(`Checked: ${label} - WCAG: ${wcagRatio.toFixed(2)}, APCA Lc: ${apcaLc.toFixed(1)}`);
}
// --- Test Suite ---
describe('Color Contrast Accessibility Checks', () => {
test('Primary Text on Primary Background meets WCAG AA', () => {
checkContrastPair(
'Primary Text/Background',
themeColors.primaryText,
themeColors.primaryBackground
);
});
test('Accent Text on Accent Background meets WCAG AA', () => {
checkContrastPair(
'Accent Text/Background',
themeColors.accentText,
themeColors.accent
);
});
test('Error Text on Error Background meets WCAG AA', () => {
checkContrastPair(
'Error Text/Background',
themeColors.errorText,
themeColors.error
);
});
test('Subtle Text on Primary Background meets WCAG AA', () => {
// This might intentionally fail if 'subtle' doesn't meet AA
// Adjust threshold or colors based on design requirements
checkContrastPair(
'Subtle Text/Background',
themeColors.subtleText,
themeColors.primaryBackground
);
});
// ... Add more tests for critical combinations
});
3. Configure package.json:
{
"scripts": {
"test": "jest",
// or
// "test": "vitest run"
"test:ci": "npm run test -- --ci --silent" // Example for CI environment
}
}
4. GitHub Actions Workflow (.github/workflows/ci.yml):
name: Node.js CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run Accessibility Tests
run: npm run test:ci # Run tests, including contrast checks
# The job will fail if any expect() fails
Option 2: Calling the Accessibility Check API
If you have deployed the API server from the previous tutorial, you can call it from your CI script.
1. Define Critical Pairs (e.g., in a script or file):
You might have a simple script or configuration file defining the color pairs you want to check.
2. GitHub Actions Workflow (.github/workflows/ci.yml):
name: Node.js CI & API Checks
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
accessibility_check:
runs-on: ubuntu-latest
# Ensure API_URL is configured (e.g., via GitHub Secrets)
env:
API_URL: ${{ secrets.A11Y_CHECK_API_URL }} # e.g., https://your-a11y-api.com/check-contrast
steps:
- uses: actions/checkout@v4
# Example: Check a single pair directly with curl
- name: Check Primary Text/Background via API
run: |
response=$(curl -s -w "%{http_code}" -X POST "$API_URL" \
-H "Content-Type: application/json" \
-d '{
"textColor": "#212121",
"backgroundColor": "#FFFFFF"
}')
http_code=$(tail -n1 <<< "$response") # Extract HTTP code
body=$(sed '$d' <<< "$response") # Extract body
if [ "$http_code" -ne 200 ]; then
echo "API Error: HTTP $http_code"
echo "$body"
exit 1
fi
# Check WCAG ratio (using jq to parse JSON)
wcag_ratio=$(echo "$body" | jq '.results.wcag.ratio')
wcag_pass=$(echo "$wcag_ratio >= 4.5" | bc -l)
echo "Checked Primary: WCAG Ratio $wcag_ratio"
if [ "$wcag_pass" -ne 1 ]; then
echo "Accessibility Check Failed: Primary Text/Background WCAG ratio ($wcag_ratio) is below 4.5"
exit 1
else
echo "Accessibility Check Passed: Primary Text/Background"
fi
# --- Add more checks for other pairs ---
# You would likely use a script here to loop through multiple pairs
# - name: Check Multiple Pairs via API Script
# run: node ./scripts/check-colors-via-api.js
(Note: The curl example is basic. A dedicated script (check-colors-via-api.js) using Node.js fetch and handling multiple pairs would be more robust.)
Failing the Build
Both approaches naturally integrate with CI:
- Option 1: If any
expect()assertion in your Jest/Vitest tests fails, the test runner exits with a non-zero code, failing the GitHub Actions step. - Option 2: The script calling the API (like the
curlexample or a Node.js script) must explicitly check the results andexit 1if a contrast check fails. This non-zero exit code fails the GitHub Actions step.
Next Steps
- Expand Coverage: Add more critical color pairs to your tests or API checks.
- Refine Thresholds: Adjust WCAG (e.g., AAA) or APCA thresholds based on specific component requirements.
- Integrate APCA: Add APCA checks (using appropriate Lc thresholds based on font context) alongside WCAG.
- Linting: Explore linters (like
stylelint-a11y) that can catch some issues statically. - Reporting: Configure test runners or scripts to produce clearer reports on failures.
By integrating automated accessibility checks into your CI/CD pipeline using @weable/a11y-color-utils (either directly or via an API), you build a more robust process for creating and maintaining accessible web applications.