@terrazzo/parser
Version:
Parser/validator for the Design Tokens Community Group (DTCG) standard.
58 lines • 2.49 kB
JavaScript
import { tokenToCulori } from '@terrazzo/token-tools';
import { wcagContrast } from 'culori';
import { docsLink } from '../lib/docs.js';
export const A11Y_MIN_CONTRAST = 'a11y/min-contrast';
export const WCAG2_MIN_CONTRAST = {
AA: { default: 4.5, large: 3 },
AAA: { default: 7, large: 4.5 },
};
export const ERROR_INSUFFICIENT_CONTRAST = 'INSUFFICIENT_CONTRAST';
const rule = {
meta: {
messages: {
[ERROR_INSUFFICIENT_CONTRAST]: 'Pair {{ index }} failed; expected {{ expected }}, got {{ actual }} ({{ level }})',
},
docs: {
description: 'Enforce colors meet minimum contrast checks for WCAG 2.',
url: docsLink(A11Y_MIN_CONTRAST),
},
},
defaultOptions: { level: 'AA', pairs: [] },
create({ tokens, options, report }) {
for (let i = 0; i < options.pairs.length; i++) {
const { foreground, background, largeText } = options.pairs[i];
if (!tokens[foreground]) {
throw new Error(`Token ${foreground} does not exist`);
}
if (tokens[foreground].$type !== 'color') {
throw new Error(`Token ${foreground} isn’t a color`);
}
if (!tokens[background]) {
throw new Error(`Token ${background} does not exist`);
}
if (tokens[background].$type !== 'color') {
throw new Error(`Token ${background} isn’t a color`);
}
// Note: if these culors were unparseable, they would have already thrown an error before the linter
const a = tokenToCulori(tokens[foreground].$value);
const b = tokenToCulori(tokens[background].$value);
// Note: for the purposes of WCAG 2, foreground and background don’t
// matter. But in other contrast algorithms, they do.
const contrast = wcagContrast(a, b);
const min = WCAG2_MIN_CONTRAST[options.level ?? 'AA'][largeText ? 'large' : 'default'];
if (contrast < min) {
report({
messageId: ERROR_INSUFFICIENT_CONTRAST,
data: {
index: i + 1,
expected: min,
actual: Math.round(contrast * 100) / 100,
level: options.level,
},
});
}
}
},
};
export default rule;
//# sourceMappingURL=a11y-min-contrast.js.map