CSS 变量 + calc() + rgb() = 强制高对比度颜色

Avatar of Josh Bader
Josh Bader

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

您可能知道,CSS 的最新更新和补充功能非常强大。从 FlexboxGrid,以及——我们这里关注的——自定义属性又名 CSS 变量),所有这些都使构建健壮和动态的布局和界面比以往任何时候都更容易,同时还打开了我们过去只能梦寐以求的许多其他可能性。

前几天,我在想,一定有办法使用自定义属性为元素的背景着色,同时保持与前景颜色足够高的对比度(使用白色或黑色)以通过 WCAG AA 可访问性标准

使用几行 JavaScript 代码来执行此操作非常高效

var rgb = [255, 0, 0];

function setForegroundColor() {
  var sum = Math.round(((parseInt(rgb[0]) * 299) + (parseInt(rgb[1]) * 587) + (parseInt(rgb[2]) * 114)) / 1000);
  return (sum > 128) ? 'black' : 'white';
}

这会获取元素背景颜色的红色、绿色和蓝色 (RGB) 值,分别将它们乘以 一些特殊数字(分别为 299、587 和 144),将它们加在一起,然后将总和除以 1,000。当该总和大于 128 时,它将返回黑色;否则,我们将获得白色。还不错。

唯一的问题是,当在 CSS 中重新创建此功能时,我们无法访问本机 if 语句来评估总和。那么,如何在没有 if 语句的情况下在 CSS 中复制此功能呢?

幸运的是,与 HTML 一样,CSS 也非常宽容。如果我们将大于 255 的值传递给 RGB 函数,它将被限制在 255。对于小于 0 的数字也是如此。即使是负整数也会被限制在 0。因此,我们无需测试总和是否大于或小于 128,而是从总和中减去 128,从而得到正整数或负整数。然后,如果我们将其乘以一个较大的负值(例如 -1,000),我们最终会得到非常大的正值或负值,然后可以将其传递给 RGB 函数。就像我之前说的,这将被限制在浏览器期望的值。

这是一个使用 CSS 变量的示例

:root {
  --red: 28;
  --green: 150;
  --blue: 130;

  --accessible-color: calc(
    (
      (
        (
          (var(--red) * 299) +
          (var(--green) * 587) +
          (var(--blue) * 114)
        ) / 1000
      ) - 128
    ) * -1000
  );
}

.button {
  color:
    rgb(
      var(--accessible-color),
      var(--accessible-color),
      var(--accessible-color)
    );
  background-color:
    rgb(
      var(--red),
      var(--green),
      var(--blue)
    );
}

如果我的数学计算正确(也有可能不正确),我们将得到 16,758 的总和,这远大于 255。将此总和传递给所有三个值的 rgb() 函数,浏览器将把文本颜色设置为白色。

添加一些范围滑块来调整 color 值,就是这样:一个动态的 UI 元素,可以根据其 background-color 交换文本颜色,同时保持通过 WCAG AA 的评分。

查看 CodePen 上的示例
仅 CSS 可访问按钮
,作者为 Josh Bader (@joshbader)
CodePen 上。

将此概念应用于实践

下面是一个 CodePen 示例,展示了如何使用此技术为用户界面设置主题。我已经复制并将 --accessible-color 变量移动到需要它的特定 CSS 规则中,并且为了帮助确保背景基于其前景保持可访问性,我在多个地方将 --accessible-color 变量乘以 -1。可以通过使用位于右下角的控件来更改颜色。单击齿轮/齿轮图标以访问它们。

查看 CodePen 上的示例
CSS 变量可访问 UI
,作者为 Josh Bader (@joshbader)
CodePen 上。

还有其他方法可以做到这一点

不久前,Facundo Corradini 解释了如何在 这篇文章 中执行非常类似的操作。他结合使用了略微不同的计算方法和 hsl 函数。他还详细介绍了在提出此概念时遇到的一些问题

某些色调会变得非常成问题(尤其是黄色和青色),因为尽管它们具有相同的亮度值,但显示出的亮度却比其他色调(例如红色和蓝色)高得多。因此,某些颜色被视为暗色,并使用白色文本,尽管它们非常亮。

CSS 到底发生了什么?

他继续提到 Edge 没有限制他的大数字,并且在我的测试过程中,我注意到有时它可以工作,而其他时候则不行。如果有人能查明为什么会这样,请随时在评论中分享。

此外,Ana Tudor 解释了如何使用 filter + mix-blend-mode 可以 帮助文本与更复杂的背景形成对比。而且,当我提到复杂时,我的意思是复杂。她甚至展示了文本颜色如何在背景颜色的部分发生变化时发生变化——非常棒!

此外,Robin Rendle 解释了如何使用 mix-blend-mode 以及伪元素来 根据其 background-color 自动反转文本颜色

因此,可以将此视为添加到混合中的另一种方法。自定义属性能够为我们打开这些可能性,同时允许我们以各种方式解决相同的问题,这真是太棒了。