Skip to content

色彩对比度矩阵检查器:把 WCAG 配对检查变成可读网格 ​

译注:本文编译自 dev.to 文章《Color Contrast Matrix Checker: Turning WCAG Pair Checks into a Readable Grid》,作者 begoodtool,原文发布于 2026 年 10 月 1 日。原文链接见文末。

当设计系统包含四、六甚至十种颜色时,无障碍审查会变得非常重复。设计师可能检查了白底深色文字,却忘了警告色在浅色卡片上的表现,或者徽章里的反色组合。作者为此构建了 Color Contrast Matrix Checker,把成对的对比度问题变成一张表格:每个输入颜色同时成为一行和一列,每个有序的前景/背景组合都会得到一个 WCAG 判定结果。

这款工具的目标读者是需要在上线组件前审查调色板的前端开发者或设计系统维护者。它既呈现计算过程,也呈现工作形态:对比度基于相对亮度,而矩阵让组合数量变得可见。需要说明的是,这只是该计算在浏览器中的一个具体实现,并不代表仅凭 RGB 就能描述所有渲染后的设计效果。

计算前先规范化调色板 ​

界面初始提供四种颜色,最多可添加至十种。每行都有原生取色器和文本输入框,但计算只接受三位或六位十六进制值:

javascript
function isValidHex(hex) {
  return /^#([0-9a-fA-F]{3}){1,2}$/.test(hex);
}

function normalizeHex(hex) {
  const clean = hex.replace(/^#/, "");
  if (clean.length === 3) {
    return "#" + clean.split("").map((c) => c + c).join("");
  }
  return "#" + clean.toLowerCase();
}

点击 calculate 时,每行在有效的情况下会从取色器同步,然后映射为规范化值。如果任何一项无效,函数会停止并显示本地化数据中的“At least 2 colors required”。实际上,界面不允许少于两种颜色,添加按钮也限制不超过十种。这个上限同时保证结果表格在小屏幕上仍然可用。

比值是亮度数学,不是通道平均 ​

源码会展开简写十六进制,并把每个通道转换为线性光值:

javascript
function linearize(c) {
  const s = c / 255;
  return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
}

function relativeLuminance([r, g, b]) {
  return 0.2126 * linearize(r) +
         0.7152 * linearize(g) +
         0.0722 * linearize(b);
}

function contrastRatio(hex1, hex2) {
  const rgb1 = hexToRgb(hex1);
  const rgb2 = hexToRgb(hex2);
  if (!rgb1 || !rgb2) return null;
  const l1 = relativeLuminance(rgb1);
  const l2 = relativeLuminance(rgb2);
  const lighter = Math.max(l1, l2);
  const darker = Math.min(l1, l2);
  return (lighter + 0.05) / (darker + 0.05);
}

0.05 项避免了零亮度颜色导致比值无定义。取较亮和较暗亮度使比值具有对称性:黑底白字与白底黑字数值相同,尽管界面仍把一种颜色标为行、另一种标为列。

阈值取决于文字大小 ​

单选按钮用于选择普通文字或大号文字,检查器随后套用源码中的 WCAG 阈值:

javascript
function getLevel(ratio, size) {
  const aaThreshold = size === "large" ? 3.0 : 4.5;
  const aaaThreshold = size === "large" ? 4.5 : 7.0;
  if (ratio >= aaaThreshold) return "aaa";
  if (ratio >= aaThreshold) return "aa";
  return "fail";
}

结果循环会刻意计算每一对的两个方向:

javascript
for (let i = 0; i < n; i++) {
  const row = [];
  for (let j = 0; j < n; j++) {
    if (i === j) {
      row.push({ same: true, bg: validColors[i], fg: "#888888", levelClass: "same" });
    } else {
      const ratio = contrastRatio(validColors[i], validColors[j]);
      const level = getLevel(ratio, size);
      row.push({
        same: false, bg: validColors[i], fg: validColors[j],
        ratio: ratio.toFixed(2) + ":1", level, levelClass: level,
      });
    }
  }
  result.push(row);
}

对角线标记为“Same”,而不是假装一种颜色与自身是有效的文字配对。汇总计数会排除这些单元格,totalPairs 为 n * (n - 1),因此总数反映的是有序的前景/背景组合。

有序网格之所以有用,是因为渲染预览并不抽象:每个单元格以行颜色为背景、列颜色为前景。设计系统负责人因此能看到正在审查的具体文字/背景分配。CSV 导出保持相同方向,表头为 FG \ BG,可以把审查带入电子表格而不丢失哪一侧是文字。

矩阵也是调试辅助工具 ​

每个单元格显示比值和 AA/AAA/fail 标签,单元格背景设为行颜色,文字颜色设为列颜色。表格可横向滚动,导出函数把相同数值写入 color-contrast-matrix.csv。这让结果在评审中很有用:一个失败单元格指向具体配对,而不是含糊的“调色板需要改进”。

工具也有诚实的边界。解析器只接受 RGB 十六进制;alpha、CSS 渐变、图片和 color-mix 结果都未建模。公式无法得知字体实际字重、抗锯齿、周围颜色或半透明叠加层。“大号”选项是与工具标签匹配的简化选择,并非解析所有 CSS 排版规则。单独通过的比值在真实组件中仍可能难以阅读。

作者已将该实现做成一个小型免费工具:Color Contrast Matrix Checker。

原文链接 ​

https://dev.to/begoodtool/color-contrast-matrix-checker-turning-wcag-pair-checks-into-a-readable-grid-35oo