让 浏览器智能选择颜色?CSS 这个新功能太聪明了!

让 浏览器智能选择颜色?CSS 这个新功能太聪明了!

技术教程gslnedu2025-06-05 15:42:152A+A-

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

1.Webkit 宣布引入 contrast-color()

2025 年 5 月 13 日,Webkit 官网发布了一个全新的 CSS 函数,即 contrast-color()。

有了 contrast-color(),开发者只需编写简单的 CSS 即可让浏览器自行判断该颜色应该搭配什么颜色,其工作原理如下:

假设需要构建一个 Web 网站,设计需要大量具有不同背景颜色的按钮,此时可以创建一个名为 --button-color 的变量来处理背景色。然后,根据不同的情况为设计系统中的变量赋值。例如:有时按钮背景色较深,文本颜色应为白色以保证对比度。有时,背景色较浅,文本颜色应为黑色。

虽然可使用新变量来指定文本颜色,并成对定义 --button-color 和 --button-text-color 以确保文本颜色符合预期。但是,在大型项目合作时会变得非常困难。

因此,如果可以直接告诉 CSS 将文本设置为黑白,并让浏览器自行决定最终颜色以保证对比度。此时,开发者就可以只管理各种背景色,而不必单独处理文本颜色。

2. 如何使用 contrast-color()

根据官方描述,该函数的使用非常简单:

color: contrast - color(purple);

在上面的代码中,浏览器会将颜色设置为黑色或白色,从而能与紫色形成更好的对比。

接下来首先设置按钮样式,即将按钮的背景色设置为变量,同时将文本颜色定义为与该变量配对的对比色黑色或白色:

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

此时开发者只需要定义一种颜色,当更改按钮颜色时,浏览器会重新考虑文本应该是黑色还是白色,并选择对比度更高的颜色。

开发者甚至可以使用 contrast-color() 定义悬停颜色,最终用一个变量决定了四种颜色,即默认按钮颜色及其对应的文本,以及悬停颜色及其对应的文本。

:root {
  --button-color: purple;
  --hover-color: oklch(from var(--button-color) calc(l + .2) c h);
  // oklch() 函数表达 Oklab 颜色空间中的给定颜色
}
button {
  background-color: var(--button-color);
  color: contrast-color(var(--button-color));
  text-box: cap alphabetic;
  /* 垂直居中 */
}
// 设置按钮的 hover 背景和文本颜色
button:hover {
  background-color: var(--hover-color);
  color: contrast-color(var(--hover-color));
}

3. 无障碍设计考量和对比度算法

很多开发者可能会误认为 contrast-color() 函数本身就能解决所有对比度无障碍问题 (Contrast Accessibility Concerns),然而事实并非如此。

使用 contrast-color() 函数并不能保证最终的颜色组合无障碍,即很有可能选择了一种与黑色或白色对比度不足的颜色,而确保对比度明显的工作仍然取决于相关人员,例如:设计师、开发者、测试人员等等。

事实上,如果在 Safari 技术预览版 (Safari Technology Preview) 中查看下面的 Demo,就会发现很多与中间色调背景颜色的搭配都无法产生足够的对比度,即浏览器做出了错误的选择。

:root {
  --button-color: purple;
}
button {
  background-color: var(--button-color);
  color: contrast-color(var(--button-color));
  text-box: cap alphabetic;
}
button:hover {
  --hover-color: oklch(from var(--button-color) calc(l + 0.2) c h);
  background-color: var(--hover-color);
  color: contrast-color(var(--hover-color));
}
:root {
  color-scheme: light dark;
  font-family: Avenir, Montserrat, Corbel, "URW Gothic", source-sans-pro,
    sans-serif;
}
main {
  height: 100vh;
  display: grid;
  place-items: center;
}
header {
}
button {
  font-family: inherit;
  border-radius: 2lh;
  padding: 0.5lh 1lh;
  border: none;
  box-shadow: 0 0 12px rgb(0, 0, 0, 0.3);
  font-size: 2rem;
  grid-area: 1 / 1;
  width: max-content;
}
header {
  grid-area: 1 / 1;
  place-self: start;
}
label {
  display: flex;
  align-items: center;
  font-weight: 500;
}
input {
  aspect-ratio: 1;
  height: auto;
  border: none;
  background-color: transparent;
  padding-inline-start: 5px;
}
figure {
}
figcaption {
  text-align: center;
  margin-block-start: 2lh;
}
dialog {
  background-color: rgb(180, 0, 0);
  box-shadow: 0 0 10px rgb(0, 0, 0, 0.6);
  color: white;
  border: 0;
  padding: 1lh;
}
dialog::backdrop {
  background-color: rgb(0 0 0 / 25%);
}
@supports (color: contrast-color(purple)) {
  dialog {
    display: none;
  }
}

下面是页面 HTML 内容:

<main>
  <header>
    <label>Button color:
      <input type="color" colorspace="display-p3"
             id="color-picker"
             value="purple">
    </label>
  </header>
  <button>This is a button</button>
<!--   <small>The button color is determined by the color picker, while the browser chooses either white or black for text color — whichever provides more contrast.</small> -->
  <dialog open class="support-warning">This browser does not support <code>contrast-color()</code>. Try this demo in a browser that does, like Safari Technology Preview.</dialog>
</main>

例如: #317CFF 蓝色背景会返回黑色的对比色,而白色显然更合适。

主要的原因在于:

  • Safari 技术预览版使用的是 WCAG 2 中正式定义的对比度算法,如果将蓝色放入 WebAIM 中颜色对比度检查器中,其明确表示使用黑色而非白色作为文本颜色。WCAG 2 是当前 Web 可访问性的权威标准,因此许多地方的法律都要求使用黑色。
  • WCAG 2 算法计算出 #317CFF 上的黑色对比度为 5.45:1,而 #317CFF 上的白色对比度为 3.84:1,contrast-color() 函数选择了前者数值较大的选项。

因此,当运行 WCAG 2 算法时,黑色文本在数学上具有更高的对比度。但当人类观看这些组合时,显然黑色对比度更低。WCAG 2 的颜色对比度算法长期以来一直饱受诟病,而将 WCAG 更新到 3 级的核心驱动力就是改进对比度算法。

无障碍感知对比度算法 (Accessible Perceptual Contrast Algorithm,APCA) 是一种预测对比度的新方法,适用于新兴的 Web 标准 (WCAG 3),用于确定可读性对比度。APCA 源自 SAPC(S-LUV 高级预测色彩),后者是一种面向无障碍的色彩外观模型,专为自发光显示器设计。

无障碍感知对比度算法 (Accessible Perceptual Contrast Algorithm ,APCA) 是可能被纳入 WCAG 3 的候选算法之一,开发者目前已经可以使用 apcacontrast.com 上的 APCA 对比度计算器试用该算法,下面是该算法在特定蓝色背景上的黑色和白色文本的感受。

该算法将蓝底黑字的 Lc 值评估为 38.7,而蓝底白字的 Lc 值则为 -70.9,忽略符号的情况下,数值越大对比度越高,最终白色文本胜出。

APCA 算法是基于感知对比度,而非简单的数学计算,其考虑到了人类感知对比度时,色相和亮度之间并非呈线性关系这一事实。

幸运的是,contrast-color() 背后的算法可以替换,此功能于 2021 年 3 月在 Safari 技术预览版 122 中首次发布。而当时,谈论更好的算法还为时过早。

CSS 标准仍然要求浏览器使用旧算法,但包含面向未来的说明:

目前仅支持 WCAG 2.1,且已知该算法存在问题,尤其是在深色背景下。该模块的未来修订版可能会引入其他对比度算法。” 关于哪种算法最适合 WCAG 3 的争论仍在进行中,包括对正在考虑的算法的许可的讨论。

因此,在选择调色板时仍需牢记可访问性,如果选择清晰明亮 (clearly-light) 的颜色或清晰明亮的深色 (clearly-dark) 作为对比色,那么 contrast-color() 即使在 WCAG 2 算法的支持下也能符合预期。但在评估与中间色调的对比度时,不同算法的结果会有所不同。

此外,即使更新算法,contrast-color() 函数本身也无法保证可访问性,很多颜色与黑色或白色的对比度永远不够,尤其是在文本尺寸较小或字体较细的情况下。

4. 在现实世界中提供足够的对比度

在考虑色彩对比度时,可以使用 prefers-contrast 媒体查询,其可以确保为每个人提供良好的对比度,即为需要更高对比度的用户提供替代样式。

.contrast {
  width: 100px;
  height: 100px;
  outline: 2px dashed black;
}
@media (prefers-contrast: more) {
  .contrast {
    outline: 2px solid black;
  }
}

prefers-contrast 支持以下属性:

  • no-preference:表示用户未向系统告知任何偏好设置,此关键字值在布尔上下文中计算为 false
  • more:表示用户已通知系统自己更喜欢对比度更高的界面
  • less:表示用户已通知系统自己更喜欢对比度较低的界面
  • custom:表示用户已通知系统使用一组特定的颜色,并且这些颜色所隐含的对比度不多不少,此值将与 forced-colors: active 的用户指定的调色板匹配

当在变量中定义颜色时,根据不同的条件替换颜色值非常容易。即使用 contrast-color() 函数只需考虑背景色,而无需考虑文本颜色的搭配,浏览器会自行处理。

为了一次性完成所有操作,开发者只需编写以下代码即可(假设会有更好的算法取代 CSS 中的 WCAG 2 算法):

--button-color: #2DAD4E;
/* 蓝色背景色 */
@media (prefers-contrast: more) {
  @media (prefers-color-scheme: light) {
    --button-color: #419543;
    /* darker green background */
  }
  @media (prefers-color-scheme: dark) {
    --button-color: #77CA8B;  /* lighter green background */
  }
}
button {
  background-color: var(--button-color);
  color: contrast-color(var(--button-color));
  font-size: 1.5rem;
  /* 1.5 * 16 = 24px at normal zoom */
  font-weight: 500;
}

实际上,由于 contrast-color() 的驱动因素是 WCAG 2 算法,最终可能无法在某些场景下使用。但如果在某些项目中,品牌颜色是深绿色,并且白色、黑色之间的选择是正确的,那么则可能会非常有用。

使用 contrast-color() 在为多个状态或选项,例如:启用 / 禁用、亮 / 暗模式、优先对比度等定义颜色时尤其有用。

5. contrast-color() 能否超越黑白

如果希望浏览器能自动选择除黑白之外的颜色怎么办? 实际上,较新的 contrast-color() 比原始的 color-contrast() 函数大大简化。

由于 WCAG 3 中应使用哪种颜色对比度算法仍在争论中,CSS 工作组 (CSS Working Group) 决定推出一款工具,只需选择黑色或白色与第一个颜色进行对比即可,保持简单方便的同时也方便未来替换。通过将选项列表硬编码为黑白,网站在 WCAG 2 算法被替换后崩溃的可能性大大降低,从而为 CSSWG 提供了所需的灵活性,即使对比度颜色算法已经交付给用户,也能继续进行必要的更新。

未来很可能会出现更复杂的工具来支持更强大的选项,或许可以列出一组自定义颜色选项,让浏览器从中选择而非只选黑白。但是提前交付简易版本,可以让开发者尽快尝鲜。

同时,虽然以上所有示例都显示了彩色背景上的黑白文本,但 contrast-color() 的用途远不止于此。其可以为文本使用自定义颜色,并将背景设置为黑白。或者完全不包含文本,只需定义边框、背景等任何颜色即可。

参考资料

声明:本文内容来自 Jen Simmons 在 2025 年 5 月 13 日发表的文章《How to have the browser pick a contrasting color in CSS》,但是对部分内容添加了自己的理解和修改。

https://webkit.org/blog/16929/contrast-color/

https://css-tricks.com/exploring-color-contrast-for-the-first-time/

https://codepen.io/jensimmons/pen/XJJjKMO?editors=1100

https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast

https://github.com/Myndex/SAPC-APCA/?tab=readme-ov-file

点击这里复制本文地址 以上内容由朽木教程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

朽木教程网 © All Rights Reserved.  蜀ICP备2024111239号-8