构建配色方案

有关如何建立动态可配置配色方案的基础概览

在本文中,我想分享一下在 CSS 中管理多种配色方案的方法。试用演示版

演示

如果您更喜欢视频,请观看 YouTube 上本帖子的视频版本:

概览

我们将使用自定义属性和 calc() 构建一个无障碍颜色系统,以便制作可根据用户偏好设置自适应的网页,同时尽可能减少编写体验。我们从基础品牌颜色开始,并据此构建一个变体系统:2 种文本颜色、4 种 Surface 颜色和一个匹配的阴影。

本指南首先介绍了如何预先定义每种配色的所有颜色。只有在最后,它们才会用于更改页面。

品牌

品牌颜色通常已确定,并以 hexrgb 格式提供。此 GUI 挑战的基准品牌颜色为 #0af。首先,对于此颜色系统,十六进制值需要转换为 hsl

* {
  --brand: #0af;
  --brand: hsl(200 100% 50%);
}

为了实现使品牌颜色变深或变浅(例如 20%)的概念,需要将 hsl 颜色值的 3 个通道提取到各自的自定义属性中,如下所示:

* {
  --brand-hue: 200;
  --brand-saturation: 100%;
  --brand-lightness: 50%;
}

CSS 可以对这些颜色属性进行数学运算,例如 calc(var(--brand-lightness) - 20%) 可将亮度值降低 20%。这是构建配色方案的基础,因为 CSS 可以通过调整 hsl 饱和度和亮度量来使所有颜色都属于同一色相族。

浅色主题

每个颜色款式都会标记其匹配的方案,在本例中,每个款式都会附加 -light

浅色主题最终结果的预览

品牌

从品牌颜色开始,通过将 --brand-hue--brand-saturation--brand-lightness 自定义属性封装在 hsl () 函数括号中来重新构建,而无需进行任何计算:

* {
  --brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness));
}

文字颜色

接下来,配色方案的必备元素需要文本颜色。在浅色主题中,文本应非常深色。请注意,以下颜色的亮度非常低,远低于 50%。

* {
  --text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%);
  --text2-light: hsl(var(--brand-hue) 30% 30%);
}

--text1-light,由于亮度为 10% 时颜色非常深,因此保持饱和度为 100% 的浓度,以便品牌颜色仍能透过深蓝色显示出来。

--text2-light,它不像第一种颜色那么深,这很好,因为它是辅色,饱和度也低得多。

Surface 颜色

Surface 颜色是指文本位于其上或其内的背景、边框和其他装饰 Surface。在浅色主题中,这些是浅色,而非深色文本颜色。如需使用 hsl 创建浅色,我们将在第三个亮度值中使用更高的百分比值。我们还会降低饱和度,以免浅灰色看起来过于偏色。

* {
  --surface1-light: hsl(var(--brand-hue) 25% 90%);
  --surface2-light: hsl(var(--brand-hue) 20% 99%);
  --surface3-light: hsl(var(--brand-hue) 20% 92%);
  --surface4-light: hsl(var(--brand-hue) 20% 85%);
}

由于装饰色通常需要更多变体(例如 :focus:hover 等互动时刻),或者用于创建纸张层的外观,因此创建了 4 种 Surface 颜色。在这些情况下,最好将悬停时的 --surface2-light 转换为 --surface3-light,这样悬停会导致对比度增加(亮度从 99% 变为 92%;使其变暗)。

阴影

配色方案中的阴影效果更胜一筹,不仅能为效果增添逼真感,还能让其从不真实的黑色阴影中脱颖而出。为此,阴影的颜色将使用色相自定义属性,色相略微饱和,但仍非常深。本质上是在构建一个非常深且略带蓝色的阴影。

* {
  --surface-shadow-light: var(--brand-hue) 10% 20%;
  --shadow-strength-light: .02;
}

--surface-shadow-light 未封装在 hsl 函数中。这是因为 --shadow-strength 值将组合起来以创建一些不透明度,而 CSS 需要这些部分才能执行计算。请跳转至“圆角阴影”部分了解详情。

浅色搭配

您无需四处寻找即可找到任何浅色是如何制作的,因为它们都在 CSS 中的一个位置。

* {
  --brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness));
  --text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%);
  --text2-light: hsl(var(--brand-hue) 30% 30%);
  --surface1-light: hsl(var(--brand-hue) 25% 90%);
  --surface2-light: hsl(var(--brand-hue) 20% 99%);
  --surface3-light: hsl(var(--brand-hue) 20% 92%);
  --surface4-light: hsl(var(--brand-hue) 20% 85%);
  --surface-shadow-light: var(--brand-hue) 10% calc(var(--brand-lightness) / 5);
  --shadow-strength-light: .02;
}
浅色主题的所有颜色的屏幕截图
CodePen 上的沙盒

深色主题

大多数品牌不会以深色主题作为起点,而是以其主要主题(通常较浅)的变体作为起点。另一方面,用户通常会针对不同的情境(例如夜间)选择深色主题。基于这些因素,我对深色主题提出了以下两点建议:

  1. 用户通常是在黑暗环境中使用此主题,因此请在黑暗环境中进行测试。
  2. 颜色应进行去饱和处理,以免因过于浓烈而导致屏幕上出现振动。

深色主题的最终结果预览

品牌

浅色主题使用了 3 个品牌 hsl 颜色通道值,而深色主题则没有。饱和度减半,亮度相对减少 50%。

* {
  --brand-dark: hsl(
    var(--brand-hue)
    calc(var(--brand-saturation) / 2)
    calc(var(--brand-lightness) / 1.5)
  );
}

文字颜色

在深色主题中,文本颜色应为浅色。以下颜色的亮度值较高,更接近白色。

* {
  --text1-dark: hsl(var(--brand-hue) 15% 85%);
  --text2-dark: hsl(var(--brand-hue) 5% 65%);
}

Surface 颜色

在深色主题中,Surface 颜色应为深色。以下颜色的亮度和饱和度较低,其中第 1 个 Surface 的亮度为 10%,最暗。

* {
  --surface1-dark: hsl(var(--brand-hue) 10% 10%);
  --surface2-dark: hsl(var(--brand-hue) 10% 15%);
  --surface3-dark: hsl(var(--brand-hue) 5%  20%);
  --surface4-dark: hsl(var(--brand-hue) 5% 25%);
}

阴影

在深色主题下,阴影可能很难看清。这很合理,因为很难让已经很暗的画面变得更暗。这时,--shadow-strength-dark 非常有用,因为它允许我们通过更改一个变量来使阴影变暗。

* {
  --surface-shadow-dark: var(--brand-hue) 50% 3%;
  --shadow-strength-dark: .8;
}

此外,请查看阴影的饱和度。您在查看界面时能注意到颜色吗?尝试从 devtools 中移除饱和度,您更喜欢哪个?

深色全部在一起

* {
  --brand-dark: hsl(var(--brand-hue) calc(var(--brand-saturation) / 2) calc(var(--brand-lightness) / 1.5));
  --text1-dark: hsl(var(--brand-hue) 15% 85%);
  --text2-dark: hsl(var(--brand-hue) 5% 65%);
  --surface1-dark: hsl(var(--brand-hue) 10% 10%);
  --surface2-dark: hsl(var(--brand-hue) 10% 15%);
  --surface3-dark: hsl(var(--brand-hue) 5%  20%);
  --surface4-dark: hsl(var(--brand-hue) 5% 25%);
  --surface-shadow-dark: var(--brand-hue) 50% 3%;
  --shadow-strength-dark: .8;
}
深色颜色一览的屏幕截图
CodePen 上的沙盒

昏暗主题

此配色方案的重点是协调亮度和饱和度。饱和度应足够高,以便仍能看清色相,但也应刚好达到对比度得分要求,因为它本来就是要呈现昏暗和低对比度的效果。

采用昏暗主题的最终结果预览

品牌

* {
  --brand-dim: hsl(
    var(--brand-hue)
    calc(var(--brand-saturation) / 1.25)
    calc(var(--brand-lightness) / 1.25)
  );
}

文字颜色

* {
  --text1-dim: hsl(var(--brand-hue) 15% 75%);
  --text2-dim: hsl(var(--brand-hue) 10% 61%);
}

Surface 颜色

* {
  --surface1-dim: hsl(var(--brand-hue) 10% 20%);
  --surface2-dim: hsl(var(--brand-hue) 10% 25%);
  --surface3-dim: hsl(var(--brand-hue) 5%  30%);
  --surface4-dim: hsl(var(--brand-hue) 5% 35%);
}

阴影

* {
  --surface-shadow-dim: var(--brand-hue) 30% 13%;
  --shadow-strength-dim: .2;
}

同时调暗所有颜色

* {
  --brand-dim: hsl(var(--brand-hue) calc(var(--brand-saturation) / 1.25) calc(var(--brand-lightness) / 1.25));
  --text1-dim: hsl(var(--brand-hue) 15% 75%);
  --text2-dim: hsl(var(--brand-hue) 10% 61%);
  --surface1-dim: hsl(var(--brand-hue) 10% 20%);
  --surface2-dim: hsl(var(--brand-hue) 10% 25%);
  --surface3-dim: hsl(var(--brand-hue) 5%  30%);
  --surface4-dim: hsl(var(--brand-hue) 5% 35%);
  --surface-shadow-dim: var(--brand-hue) 30% 13%;
  --shadow-strength-dim: .2;
}
所有昏暗颜色的屏幕截图
CodePen 上的沙盒

易于分辨的颜色

请注意,深色文本颜色集的亮度最低值为 65%,深色 Surface 的亮度最高值为 25%。也就是说,两者之间有 40% 的亮度余量。在浅色主题中,留有 55% 的留白。将文本颜色与 Surface 颜色之间的亮度差异保持在 40-50% 左右有助于保持较高的颜色对比度比率,同时也是在得分较低时进行微调的细微杠杆。

我称之为“不断调整亮度值,直到通过”,即不断调整亮度值,直到工具显示通过为止。

按 Shift + 向下键以降低亮度并提高对比度,直到通过测试

此挑战中创建的每个主题都通过了对比度得分测试。其中,昏暗配色方案的对比度最低,但仍符合最低要求。为了帮助团队中的其他成员使用合适的对比色,最好创建一个将 Surface 颜色与无障碍文本颜色搭配使用的类名称。

.surface1 {
  background-color: var(--surface1);
  color: var(--text2);
}

.surface2 {
  background-color: var(--surface2);
  color: var(--text2);
}

.surface3 {
  background-color: var(--surface3);
  color: var(--text1);
}

.surface4 {
  background-color: var(--surface4);
  color: var(--text1);
}
昏暗 Surface 和文本配对的屏幕截图
使用 VisBug 显示的昏暗 Surface 和文本配对的屏幕截图

Rad Shadow

这些主题使用名为 .rad-shadow 的实用程序类。此阴影是使用此平滑阴影工具生成的,非常感谢。我使用它生成的代码段,并使用自己的颜色和不透明度计算对其进行了自定义。这样做是为了创建一个阴影,以便我在每个配色方案中进行调整。

每个阴影彼此相邻

为此,我为每个配色方案创建了 2 个可调整的变量:阴影颜色和阴影强度。颜色用于调整饱和度和深度,而强度则用于在深色配色方案中轻松提高阴影强度。最终结果如下所示。

:root {
  --surface-shadow-light: var(--brand-hue) 10% 20%;
  --shadow-strength-light: .02;
}

.rad-shadow {
  box-shadow:
    0 2.8px 2.2px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .03)),
    0 6.7px 5.3px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .01)),
    0 12.5px 10px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .02)),
    0 22.3px 17.9px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .02)),
    0 41.8px 33.4px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .03)),
    0 100px 80px hsl(var(--surface-shadow) / var(--shadow-strength))
  ;
}

如果我要在配色方案中进一步使用阴影,我也会将阴影角度设为设计令牌常量,因为设计中的所有阴影的光线方向都应相同。

使用配色方案

颜色预定义完毕后,接下来将其转换为与方案无关的属性。我的意思是,作为此配色方案项目中的 CSS 作者,您很少需要访问特定配色方案的值。我希望让用户能够轻松地保持主题一致。

为此,应仅通过通用自定义属性(我们稍后会进行定义)使用配色方案。这样一来,使用设计变量的用户无需担心当前设置的是哪种配色方案,只需使用 Surface 和文本颜色即可。请改用 color: var(--text1),而不是 color: var(--text1-light)。所有颜色自适应和调整都是在 CSS 中更高级别完成的。

深入了解以下代码块中浅色主题的连接样式,将通用自定义属性与浅色主题专用颜色相关联。现在,所有 var(--brand) 用法都将使用浅色品牌颜色。

浅色主题(自动)

:root {
  color-scheme: light;
  --brand: var(--brand-light);
  --text1: var(--text1-light);
  --text2: var(--text2-light);
  --surface1: var(--surface1-light);
  --surface2: var(--surface2-light);
  --surface3: var(--surface3-light);
  --surface4: var(--surface4-light);
  --surface-shadow: var(--surface-shadow-light);
  --shadow-strength: var(--shadow-strength-light);
}

该网站目前使用的是浅色主题。这是一次非常有趣的成功体验! 我们再来体验几次这种情况,在其他配色方案上下文中使用预定义的颜色。

深色主题(自动)

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;

    --brand: var(--brand-dark);
    --text1: var(--text1-dark);
    --text2: var(--text2-dark);
    --surface1: var(--surface1-dark);
    --surface2: var(--surface2-dark);
    --surface3: var(--surface3-dark);
    --surface4: var(--surface4-dark);
    --surface-shadow: var(--surface-shadow-dark);
    --shadow-strength: var(--shadow-strength-dark);
  }
}

浅色主题

[color-scheme="light"] {
  color-scheme: light;

  --brand: var(--brand-light);
  --text1: var(--text1-light);
  --text2: var(--text2-light);
  --surface1: var(--surface1-light);
  --surface2: var(--surface2-light);
  --surface3: var(--surface3-light);
  --surface4: var(--surface4-light);
  --surface-shadow: var(--surface-shadow-light);
  --shadow-strength: var(--shadow-strength-light);
}

深色主题

[color-scheme="dark"] {
  color-scheme: dark;

  --brand: var(--brand-dark);
  --text1: var(--text1-dark);
  --text2: var(--text2-dark);
  --surface1: var(--surface1-dark);
  --surface2: var(--surface2-dark);
  --surface3: var(--surface3-dark);
  --surface4: var(--surface4-dark);
  --surface-shadow: var(--surface-shadow-dark);
  --shadow-strength: var(--shadow-strength-dark);
}

暗色主题

[color-scheme="dim"] {
  color-scheme: dark;

  --brand: var(--brand-dim);
  --text1: var(--text1-dim);
  --text2: var(--text2-dim);
  --surface1: var(--surface1-dim);
  --surface2: var(--surface2-dim);
  --surface3: var(--surface3-dim);
  --surface4: var(--surface4-dim);
  --surface-shadow: var(--surface-shadow-dim);
  --shadow-strength: var(--shadow-strength-dim);
}

至此,作者可以根据需要自由使用提供的配色方案通用项,而无需再担心主题。

总结

现在您已经知道我是如何做到的,您会怎么做呢?🙂

让我们多元化我们的方法,了解在 Web 上构建的所有方式。 创建一个 Codepen 或托管自己的演示,然后在推特上向我发送,我会将其添加到下方的“社区混剪作品”部分。

来源

社区混剪内容 - @chris-kruiningno-preferencemoreless 添加了色相滑块、状态颜色和对比度模式:演示