UNPKG

@terrazzo/parser

Version:

Parser/validator for the Design Tokens Community Group (DTCG) standard.

58 lines 2.49 kB
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