构建配色方案

简要介绍如何建立可配置的动态配色方案

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

演示

如果你更喜欢视频,可以参考本博文的 YouTube 版本:

概览

我们将使用自定义属性和 calc() 构建一个易于访问的颜色系统,制作出可适应用户偏好的网页,同时保持极简的创作体验。我们从基本品牌颜色入手,并根据该颜色构建一个变体系统:2 种文本颜色、4 种 Surface 颜色和与之匹配的阴影。

本指南首先要预先定义每种配色方案的所有颜色。直到最后,它们才会用于更改页面。

品牌

通常,品牌颜色已确定并以 十六进制rgb 的形式提供。此 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,它不像第 1 种颜色那样深,因为它是次要颜色,饱和度也更低。

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%);
}

系统创建了 4 种表面颜色,因为装饰性颜色往往需要更多变体,以实现 :focus:hover 等互动时刻或打造纸层的外观。在这些情况下,最好在悬停时将 --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 需要这些部分才能执行计算。如需了解详情,请跳至 rad shadow 部分

全部灯光颜色

无需四处搜寻所有浅色的制作方式,它们都在 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 颜色应采用深色。以下颜色的亮度和饱和度较低,第一个表面最暗,为 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;
}

另外,请查看该阴影中的饱和度。当您查看界面时,有没有注意到颜色?你可以尝试从开发者工具中移除饱和度,你更喜欢哪种?!

全都采用深色

* {
  --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%,深色表面中最高亮度为 25%。这相当于 40% 的亮度呼吸空间在浅色主题中,浅色主题有 55% 的呼吸空间。将文字颜色和界面颜色之间的亮度差异保持在 40-50% 左右有助于保持较高的色彩对比度,同时在得分不佳时也是一个微小的调整手段。

我称之为“bump bump til ya pass”,即提升亮度值,直到工具显示我正在传递。

按 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);
}
暗表面和文本对的屏幕截图
使用 VisBug 对暗界面和文本进行配对的屏幕截图

雷光

这些主题使用一个名为 .rad-shadow 的实用程序类。该阴影是使用此 Smooth 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 或托管自己的演示,在 Twitter 上向我提出,我会将其添加到下面的“社区混剪”部分。

来源

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