你以为按钮背景色配白字还是黑字只能靠猜?今天 CSS 自己会算了

设计系统的人大概都有过这个经历:设计师给你八个按钮颜色——primary、success、warning、danger……让你保证文字在任何背景上都清晰可读。你怎么配?

手动查对比度工具,一个一个填。设计师哪天把品牌蓝改了,你全部重新算一遍。用户自己选了主题色,你还得写一整套 if-else luminance 判断。

这根本不是前端工程师该干的事。

现在浏览器自己会算了。

对比色这个老问题

给背景配文字颜色,本质上是在问:给定一个背景色,我该用黑色还是白色才能让文字可读?

这其实是个数学题。WCAG 2.1 定义了 relative luminance 的计算公式:

L = 0.2126 × R + 0.7152 × G + 0.0722 × B

然后用对比度公式得出比值。达到 4.5:1 以上才算 AA 级可用。

以前这个逻辑要么靠手查、要么靠 Sass 函数算、要么写一段 JavaScript。CSS 本身是帮不上忙的——直到现在。

contrast-color() 来了

contrast-color() 是 CSS Color Level 5 里的新函数,今年四月正式进入 Baseline 2026,Chrome 147、Firefox 146、Safari 26、Edge 147 全部支持。你现在就可以用。

它的用法简单到不可思议:

button {
  background-color: var(--brand-color);
  color: contrast-color(var(--brand-color));
}

传进去任何一个颜色,它会自己算出这个背景配黑色还是白色更清晰,返回的就是那个值。任何有效的 CSS 颜色都能传——十六进制、颜色名、custom property、color-mix() 的结果,悉听尊便。

这个函数最初是 CSS 工作组里 Una Kravets 在 2019 年提的提案,经过多年讨论终于落地。

实际用法

设计系统按钮组件:

.btn {
  padding: 0.5rem 1.25rem;
  border: none;
  border-radius: 0.5rem;
  font-weight: 600;
  background-color: var(--btn-color);
  color: contrast-color(var(--btn-color));
}

不管 --btn-color 今天是紫色、黄色还是用户自定义的奇怪品牌色,文字颜色永远自动选最清晰的那个。

暗色模式联动:

:root {
  --surface: #f5f5f5;
  --text: contrast-color(#f5f5f5);
}
@media (prefers-color-scheme: dark) {
  :root {
    --surface: #1a1a1a;
  }
}
body {
  background-color: var(--surface);
  color: var(--text);
}

背景随系统暗色模式切换,文字颜色自动跟着变,一行 JS 都不用写。

悬停动态变色:

.card {
  background: var(--accent);
  color: contrast-color(var(--accent));
  transition: background 0.2s;
}
.card:hover {
  background: color-mix(in oklch, var(--accent) 70%, white);
  /* 背景变浅,文字自动从白切换到黑 */
}

配合 color-mix() 可以做出悬停时背景变亮、文字自动从白变黑的动态效果。颜色渐变链路上的每一步,文字对比度始终保持可读。

有一个坑要注意

这个函数只返回黑色或白色,不是任意颜色。好处是简单,坏处是——中间调背景它救不了

比如 #2277d3 这个中蓝色,用 contrast-color() 会返回黑色文字,但这个蓝色配黑字其实达不到 WCAG AA 对小字的要求(对比度约 3.9:1)。函数不会报错,只是结果不可读。

官方文档的建议是:用它处理明显偏亮或明显偏暗的背景,不要幻想它能解决所有中间调问题。如果你有中间调配色需求,还是需要自己验证对比度,或者考虑用 OKLCH 的 lightness 反转来做更精细的控制。

渐进增强写法

所有新 CSS 特性推荐写法:

.btn {
  background-color: var(--btn-color);
  color: #fff; /* 旧浏览器兜底 */
}
@supports (color: contrast-color(red)) {
  .btn {
    color: contrast-color(var(--btn-color));
  }
}

先写一个旧浏览器的 fallback,再用 @supports 在支持的浏览器里升级。线上出了兼容问题也不影响可读性。

怎么验证

浏览器 DevTools 里直接看 computed style,contrast-color() 在支持的环境里会显示最终算出来的具体颜色值(#000000 或 #ffffff),可以逐个组件验证对比度是否达标。

下一步

如果你正在维护一个设计系统或者主题组件库,把那些手写的 luminance 判断逻辑换成这个函数,只需要两分钟。背景色的自定义能力会立刻提升一个档次——设计师改品牌主色不需要再找你对一遍对比度,用户选什么颜色文字都是清晰的。

前端排版和 UI 这件事,CSS 一直在替我们把活儿抢走。现在对比色也算一个了。

评论区

0 条评论

登录后可评论。