KAIROS.WORKSPACE
技术宇宙 / ONLINE
返回文章列表
前端工程intermediate7 分钟

暗色主题下的对比度:为什么"看着还行"往往不合格

暗色 UI 里低对比文字、彩色强调按钮、以及级联层导致的样式覆盖如何悄悄违反 WCAG 对比度,附定位与量化修复。

#可访问性#暗色主题#CSS#对比度
排版
字号
行宽
目录 · 5 节

暗色主题好看,但它是无障碍对比度的高发区。很多配色在编辑器里“看着挺清楚”,用审计工具一测却全线不及格——而且不及格的方式很隐蔽。

标准是什么

WCAG 对比度要求(相对亮度比值):

  • 正文(< 18pt / 常规字重):至少 4.5:1
  • 大文本(≥ 18pt 或 14pt 粗体):至少 3:1
  • 图标、边框、表单控件等“非文本 UI”:至少 3:1

暗色主题最常见的失分点是“低透明度灰字”:rgba(255,255,255,0.5) 叠在 #0b0e14 上看着柔和,实测常只有 6.x:1 尚可;但一旦叠加在彩色卡片、或透明度再降一档到 0.4,很容易跌破 4.5。而 text/70text/50 这类 Tailwind 半透明写法在深底上尤其危险。

一个隐蔽杀手:级联层覆盖

一次真实的暗色站审计里发现:“订阅”按钮文字对比度只有 1.73:1(严重不达标),但组件源码写的是明确的 text-white。根因是一条未分层的裸样式:

a {
  color: inherit;
} /* 未分层 → 特异性 + 层级双高压过所有 utilities */

在 Tailwind 的 @layer utilities 下,一条未分层a { color: inherit } 会赢过分层工具类,让所有 <a class="text-white"> 静默变成继承来的低对比灰。修复不是加 !important,而是把 html/body/a/:focus-visible 这类基础样式正确放进 @layer base,让 utilities 层能正常覆盖它们:

@layer base {
  a {
    color: inherit;
  }
  :root {
    /* token 定义刻意保持未分层,确保组件能读取变量 */
  }
}

暗色主题 + 半透明文字 + 级联层冲突三者叠加,能制造“肉眼觉得还行、工具判不合格、且不知道为什么”的三重坑。

量化,而不是目测

不要凭眼睛。把计算式对比度纳入流程:

function contrast(rgb1, rgb2) {
  const lum = ([r, g, b]) => {
    const a = [r, g, b].map((v) => {
      v /= 255;
      return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
    });
    return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2];
  };
  const [l1, l2] = [lum(rgb1), lum(rgb2)].sort((a, b) => b - a);
  return (l1 + 0.05) / (l2 + 0.05);
}

更省事的是直接拿计算样式喂审计:

const cs = getComputedStyle(el);
// 解析 cs.color 与逐层上溯的不透明背景,算对比度;或用 axe-core 的 color-contrast 规则

Lighthouse / axe 的 color-contrast 规则能自动化这件事,但前提是你真的去跑——很多“暗色主题上线即不合格”只是因为从没跑过。

暗色配色的实操建议

  • 正文别用纯白 #fff(深底上刺眼),用 #e6e6e6 一类高灰阶,既舒适又轻松过 4.5:1;
  • 强调色做双档:一个用于大面积(对比适中),一个用于文字/图标的“加强版”(保证 ≥4.5,图标 ≥3);
  • 半透明文字设下限:muted 不低于 0.6 透明度,低于它改用实心低饱和灰;
  • 边框/分隔线属于非文本 UI,≥3:1,rgba(255,255,255,0.08) 这种几乎不可见的分隔线其实不合规,需要提到 0.12–0.16。

小结

暗色主题的对比度不是“选个深背景”就完事,它要正向处理三件事:低透明度文字的量化、级联层不打架、强调色的可用性双档。把计算式对比度当成和“构建通过”同级的硬性门禁,“看着还行”就不会在真实用户(尤其低视力用户)那里变成“根本看不清”。

Conversation

评论与互动

正在加载评论…

提交后需审核,不会立即公开。

Keep exploring

继续探索