Playwright + axe-core
import AxeBuilder from '@axe-core/playwright';
// Check all color contrast violations
const results = await new AxeBuilder({ page })
.withRules(['color-contrast'])
.analyze();
for (const violation of results.violations) {
console.log('Contrast issues:');
for (const node of violation.nodes) {
console.log({
element: node.target,
issue: node.failureSummary
});
}
}
// Verify specific element contrast
const button = page.getByTestId('button-example');
const styles = await button.evaluate(el => ({
color: getComputedStyle(el).color,
bg: getComputedStyle(el).backgroundColor
}));Contrast Calculation
// Calculate contrast ratio programmatically
function getContrastRatio(fg: string, bg: string) {
const luminance = (hex: string) => {
const rgb = parseInt(hex.slice(1), 16);
const r = ((rgb >> 16) & 0xff) / 255;
const g = ((rgb >> 8) & 0xff) / 255;
const b = (rgb & 0xff) / 255;
const linear = (c: number) =>
c <= 0.03928
? c / 12.92
: Math.pow((c + 0.055) / 1.055, 2.4);
return 0.2126 * linear(r)
+ 0.7152 * linear(g)
+ 0.0722 * linear(b);
};
const l1 = luminance(fg);
const l2 = luminance(bg);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}