你以为品牌色只能靠人眼配对比度?今天 CSS 一个函数自己算完了

配了多年设计系统,每次品牌色一换,就要回头把所有文字颜色过一遍——深色 brand 配白字,浅色 brand 配黑字,靠人眼判断哪个能过 WCAG AA。这件事,今天 CSS 用一个函数自己算完了。

contrast-color()CSS Color Level 5 的新函数,Chrome 147+/Firefox 146+/Safari 26+ 全部支持。它接收一个背景色,返回「在这块背景上显示对比度最高的文字色」——只有两个可能:纯黑或纯白。

/* 之前:两套变量,靠人判断 */
.btn-light  { background: #e0e0e0; color: #111; }
.btn-dark   { background: #1a1a2e; color: #fff; }

/* 之后:一个变量,浏览器自己算 */
.btn {
  background: var(--brand, #2E69E8);
  color: contrast-color(var(--brand, #2E69E8));
}

品牌色是 #2E69E8 时返回 #ffffff,是 #f5f0e8 浅米色时返回 #000000,换主题色只需要改一个变量。

它解决的是这个具体问题:当你用 CSS 变量管理品牌色,而不是 hardcode 固定颜色时,换肤场景下文字颜色没有静态对应关系——你不知道这个变量最终落在哪个亮度区间。传统方案要不在构建时算好两套变量,要不运行时用 JS 算 getContrastRatio,都要写代码。contrast-color() 把这个计算下放到了渲染引擎。

三个边界要注意:

只返回黑或白,没有中间地带。 如果你需要灰色系 disabled 态,或者既不是纯黑也不是纯白的中间色,这个函数帮不上忙。设计系统里 disabled/placeholder 这类状态还是要单独处理。

/* 这个函数只管「在背景上最显眼的文字色」 */
color: contrast-color(var(--brand));     /* ✅ 主按钮文字 */
color: contrast-color(var(--disabled));  /* ❌ 不适合,disabled 需要灰色 */

不支持渐变和图片。 如果背景是 linear-gradient() 或者 url(),函数会静默失效,回退到默认值。真实场景里品牌色通常还是纯色,这限制影响不大,但做 landing page hero 这种大图背景上的文字时要额外处理。

高对比度模式要单独适配。 forced-colors: active 下浏览器会强制替换所有文字色为系统色板,contrast-color() 的结果会被覆盖。组件里最好保留 explicit 色做降级:

.btn {
  background: var(--brand);
  color: #fff; /* forced-colors 降级 */
}
@supports (color: contrast-color(red)) {
  .btn { color: contrast-color(var(--brand)); }
}

下一步:翻出项目里的按钮组件,把 color: #fffcolor: #111 的 hardcode 换成 contrast-color(var(--btn-bg)),删掉对应的 JS 计算逻辑。

评论区

0 条评论

登录后可评论。