首页
/ freeCodeCamp 实战:用 WCAG 对比度标准修复低对比度文字,让网页内容真正可读

freeCodeCamp 实战:用 WCAG 对比度标准修复低对比度文字,让网页内容真正可读

2026-09-06 09:02:21作者:胡唯隽

本篇指南基于 freeCodeCamp 响应式设计课程(Responsive Web Design)中 Applied Accessibility 板块的第 16 关 “Improve Readability with High Contrast Text”。你将理解 WCAG 推荐的 4.5:1 文本对比度标准背后的相对亮度(relative luminance)计算原理,学会修改 CSS color 属性将 1.5:1 的低对比度配色提升到 6:1,并了解本仓库中该题目的验证逻辑与后续进阶挑战的完整脉络。

主题背景:它为什么重要

前景色与背景色之间的对比度过低,会让文字变得难以辨认。对比度足够,内容的可读性才会真正提高——但“足够”到底是多少?

《Web 内容无障碍指南》(Web Content Accessibility Guidelines, WCAG)对正常字号文本推荐至少 4.5:1 的对比度。该比值通过比较两种颜色的相对亮度(relative luminance)计算得出,取值范围是:

  • 1:1 —— 同一颜色,即完全没有对比;
  • 21:1 —— 白底黑字,最大对比度。

由于手工计算相对亮度涉及伽马校正和加色模型,实际开发中通常会使用在线的对比度检测工具来自动算出这个比值。本仓库中另有专门的教学内容演示了这一计算过程,后文会给出参考。

这道题目位于课程结构文件 curriculum/structure/blocks/applied-accessibility.json 所列的 21 个 Applied Accessibility 挑战中的第 16 位(id: 587d778e367417b2b2512aab),属于 curriculum/structure/superblocks/responsive-web-design.json 定义的大板块之一。整个 Applied Accessibility 板块从图片替代文本、标题层级、landmark 元素讲到颜色对比与链接可访问性,本题正是“视觉无障碍”部分(灰度对比)的起点。

练习场景:Camper Cat 的浅灰博客文字

题目给出的初始页面(seed 代码)中,Camper Cat 的博客用了浅灰文字配白色背景:

<head>
  <style>
  body {
    color: #D3D3D3;
    background-color: #FFF;
  }
  </style>
</head>
<body>
  <header>
    <h1>Deep Thoughts with Master Camper Cat</h1>
  </header>
  <article>
    <h2>A Word on the Recent Catnip Doping Scandal</h2>
    <p>The influence that catnip has on feline behavior is well-documented, and its use as an herbal supplement in competitive ninja circles remains controversial. Once again, the debate to ban the substance is brought to the public's attention after the high-profile win of Kittytron, a long-time proponent and user of the green stuff, at the Claw of Fury tournament.</p>
    <p>As I've stated in the past, I firmly believe a true ninja's skills must come from within, with no external influences. My own catnip use shall continue as purely recreational.</p>
  </article>
</body>

这组配色的对比度只有 1.5:1,远低于 4.5:1 的 WCAG 推荐值,读起来非常吃力。题目要求把 bodycolor 从浅灰 #D3D3D3 改为更深的灰色 #636363,使对比度提升到 6:1

修改方法:只动 color,不动背景

完整的解决方案只需改动一处:

<head>
  <style>
  body {
    color: #636363;
    background-color: #FFF;
  }
  </style>
</head>

这里的关键操作是把 #D3D3D3 替换为 #636363,而 background-color: #FFF; 保持原样。

#636363 换算成十进制正是 RGB (99, 99, 99),浏览器计算样式(computed style)中会显示为 rgb(99, 99, 99)。这一点恰好对应题目内置的验证逻辑,即题目 frontmatter 与 --hints-- 部分给出的两段断言:

// 验证 1:body 的文字颜色必须是深灰 rgb(99, 99, 99),即 #636363
const body = document.querySelector('body');
const bodyColor = window.getComputedStyle(body).color;
assert(bodyColor == 'rgb(99, 99, 99)');
// 验证 2:body 的背景色必须保持白色 rgb(255, 255, 255),不能顺手改动
const body = document.querySelector('body');
const backgroundColor = window.getComputedStyle(body).backgroundColor;
assert.equal(backgroundColor , 'rgb(255, 255, 255)');

从源码结构看,这两段断言说明本关考察的是**“最小化修改”**:只允许调整文字颜色,背景色一旦改变(哪怕也提升了可读性)同样会判错。原始题目文件位于 curriculum/challenges/english/blocks/applied-accessibility/587d778e367417b2b2512aab.md,其中 --hints----seed----solutions-- 三段分别对应“验证条件 / 初始代码 / 标准答案”。

原理解深:对比度到底是怎么算出来的

题目描述提到“比值通过比较两种颜色的相对亮度计算”,但没有展开公式。本仓库的 Daily Coding Challenge(JavaScript 版)中有一组题目完整实现了 WCAG 的相对亮度算法,可作为原理印证。

Challenge 354 给出的标准计算步骤是:

  1. 将 RGB 每个通道除以 255,归一化到 01
  2. 对每个通道做伽马校正:
    • 若通道值 <= 0.04045channel / 12.92
    • 否则:((channel + 0.055) / 1.055) ^ 2.4
  3. 加权求和得到相对亮度:0.2126 * R + 0.7152 * G + 0.0722 * B(绿色权重最高,对应人眼对绿光最敏感)
  4. 对比度 = (较亮亮度 + 0.05) / (较暗亮度 + 0.05)

对应的参考实现(来自该题目的 --solutions--):

function toLuminance([r, g, b]) {
  return [r, g, b].map(channel => {
    channel = channel / 255;
    return channel <= 0.04045
      ? channel / 12.92
      : ((channel + 0.055) / 1.055) ** 2.4;
  }).reduce((sum, c, i) => sum + c * [0.2126, 0.7152, 0.0722][i], 0);
}

const l1 = toLuminance(rgb1); // 较亮颜色的相对亮度
const l2 = toLuminance(rgb2); // 较暗颜色的相对亮度
const ratio = (l1 + 0.05) / (l2 + 0.05);

姊妹题 Challenge 353 则展示了拿到比值之后如何评级:

评级 正常文本 大文本
"AAA" 7.0+ 4.5+
"AA" 4.5+ 3.0+
"Fail" 低于 4.5 低于 3.0

由此可以对号入座:

  • 本题 seed 配色 #D3D3D3 on #FFF1.5:1,不达标;
  • 修改后的 #636363 on #FFF6:1,达到“AA”甚至接近“AAA”(7:1)水平;
  • 本文档强调的 4.5:1 正是上表中正常文本达到 AA 的下限,而 21:1(黑白)就是理论上界。

进阶衔接:用 HSL 调节彩色的对比度

本关处理的是灰度场景。紧随其后的下一关 “Avoid Colorblindness Issues by Using Sufficient Contrast”(curriculum/challenges/english/blocks/applied-accessibility/587d778f367417b2b2512aac.md)把同一套标准扩展到彩色场景,值得提前了解其方法:

  • 4.5:1 的标准对彩色与灰度组合同样适用
  • hsl(hue, saturation%, lightness%) 声明颜色时,只改第三个参数 lightness 即可调节明暗:把较暗的颜色 shading(加黑)、较亮的颜色 tinting(加白);
  • 该关的具体操作是把 background-color 的 lightness 从 35% 提到 55%、color 的 lightness 从 20% 降到 15%,对比度从 2.5:1 提升到 5.9:1。

再往后一关则会处理“黄底绿字”这类色盲友好的色相选择问题,整个三连构成了“灰度对比 → 彩色明度 → 色相选择”的完整递进。

小结与可复用的检查清单

把这道 Applied Accessibility 题目拆解开来,落到工程实践就是一条可复制的检查清单:

  1. 先测量:对正文的主文字色与背景色,用对比度工具(或在 DevTools 中查看)算出当前比值;
  2. 对照标准:正常文本 < 4.5:1 即需要整改,追求更高可读性可逼近 7:1;
  3. 最小化改动:像本题一样只改 color(或只调 HSL 的 lightness),保留 background-color 等其他声明不动,既保证验证通过,也避免破坏设计系统的整体基调;
  4. 用计算样式验证window.getComputedStyle(el).color 返回的是 rgb() 形式,十六进制 #636363 对应 rgb(99, 99, 99),排查时注意两种表示的换算。

参考文件:题目原文见 587d778e367417b2b2512aab.md,板块顺序见 applied-accessibility.json,亮度计算实现见 Challenge 353Challenge 354

登录后查看全文
热门项目推荐
相关项目推荐