Skip to content

Commit cfa9530

Browse files
committed
Quick proof of concept contrast tests
1 parent 15344c2 commit cfa9530

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

package-lock.json

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
"chalk": "^5.3.0",
9292
"change-case": "^4.1.2",
9393
"chokidar": "^3.5.3",
94+
"colorjs.io": "^0.6.0-alpha.1",
9495
"command-line-args": "^5.2.1",
9596
"comment-parser": "^1.4.1",
9697
"cspell": "^6.18.1",

src/styles/color/contrast.test.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Get a list of all CSS files in repo
2+
import chalk from 'chalk';
3+
import Color from 'colorjs.io';
4+
import fs from 'fs';
5+
import path from 'path';
6+
import { fileURLToPath } from 'url';
7+
8+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
9+
10+
const paletteFiles = fs.readdirSync(__dirname).filter(file => file.endsWith('.css'));
11+
12+
function parse(contents) {
13+
// Regex for each declaration
14+
let regex = /^\s*--wa-color-(?<hue>[a-z]+)-(?<level>[0-9]+):\s*(?<color>[^;]+)\s*;$/gm;
15+
let matches = [...contents.matchAll(regex)];
16+
17+
if (matches.length === 0) {
18+
throw new Error('Cound not extract colors');
19+
}
20+
21+
let ret = {};
22+
23+
for (let match of matches) {
24+
let { hue, level, color } = match.groups;
25+
ret[hue] ??= {};
26+
level = level.replace(/^0+/, ''); // Leading zeroes throw off sorting
27+
ret[hue][level] = color;
28+
}
29+
30+
return ret;
31+
}
32+
33+
let targetContrasts = {
34+
40: 3,
35+
50: 4.5,
36+
60: 7,
37+
};
38+
39+
let result = { pass: 0, fail: 0, invalid: 0 };
40+
41+
for (let file of paletteFiles) {
42+
let css = fs.readFileSync(path.join(__dirname, file), 'utf8');
43+
let filePrefix = chalk.dim(`[${file}]`);
44+
let tokens = parse(css);
45+
46+
for (let hue in tokens) {
47+
let tints = tokens[hue];
48+
49+
for (let tint = 10; tint <= 50; tint += 10) {
50+
let color;
51+
52+
try {
53+
color = new Color(tints[tint]);
54+
} catch (e) {
55+
console.error(`[${file}] Invalid color ${hue}-${tint}: ${tints[tint]}`);
56+
result.invalid++;
57+
continue;
58+
}
59+
60+
for (let difference in targetContrasts) {
61+
let targetContrast = targetContrasts[difference];
62+
let tint2 = tint + Number(difference);
63+
if (tint2 > 90) {
64+
continue;
65+
}
66+
67+
let color2;
68+
try {
69+
color2 = new Color(tints[tint2]);
70+
} catch (e) {
71+
if (tint2 > 50) {
72+
// If 50, we'll look at it at some point as color1
73+
console.error(`${filePrefix} Invalid color ${hue}-${tint2}: ${tints[tint2]}`);
74+
result.invalid++;
75+
}
76+
continue;
77+
}
78+
79+
let contrast = color.contrast(color2, 'WCAG21');
80+
let pass = contrast >= targetContrast;
81+
if (pass) {
82+
result.pass++;
83+
} else {
84+
result.fail++;
85+
console.log(
86+
chalk.red(
87+
`${filePrefix} WCAG 2.1 contrast between ${hue}-${tint} and ${hue}-${tint2} is ${contrast.toLocaleString('en')} < ${targetContrast}`,
88+
),
89+
);
90+
}
91+
}
92+
}
93+
}
94+
}
95+
96+
let testCount = result.pass + result.fail;
97+
console.info(
98+
`Ran ${testCount} tests: ${chalk.green(`${chalk.bold(result.pass)} passed`)}` +
99+
(result.fail ? `, ${chalk.red(`${chalk.bold(result.fail)} failed`)}` : '') +
100+
(result.invalid ? `, ${chalk.red(`${chalk.bold(result.invalid)} invalid colors`)}` : ''),
101+
);
102+
103+
if (testCount === result.pass) {
104+
process.exit(0);
105+
} else {
106+
process.exit(1);
107+
}

0 commit comments

Comments
 (0)