Text reading Creating color palettes with the CSS color-mix() function. A vibrant gradient behind the artwork of CSS in the top right corner and a graphic of a website with a color palette in the bottom left corner.

使用 CSS color-mix() 函数创建调色板

作者头像Michelle Barker阅读时间:8 分钟

在项目中,颜色有时会变得难以控制。我们通常会从一些精心挑选的品牌颜色开始,但随着时间的推移,随着项目的增长,我们可能会发现自己添加了各种变化。也许我们意识到需要调整按钮颜色的亮度以满足可访问性要求,或者需要组件的稍微不同的变体。我们如何确保我们选择的颜色适合项目的 desain 系统?

我一直在探索为此目的使用相对较新的 CSS color-mix() 函数。看到我可以生成的不同的调色板变化很有趣!让我们深入了解 color-mix() 如何改变你的 desain 流程。

熟悉 color-mix() 函数

color-mix() 函数允许我们指定我们想要混合的两种颜色,然后输出结果。我们可以控制混合中每种颜色的数量,以及颜色插值空间,它决定了颜色如何融合在一起。

A graphic showing the components of a color-mix() function, namely an interpolation method and two colors along with their percentages

颜色插值方法是必需的参数。我们将在后面的部分介绍它。现在,我们将使用 srgb 来演示示例。

我们将每种颜色的数量指定为百分比。如果我们省略两种颜色的百分比,color-mix() 将默认使用 50%。如下所示,以相等比例混合 redblue 会得到预期的 purple 色调。

css
.result {
  background-color: color-mix(in srgb, blue, red);
}

如果我们只为一种颜色指定百分比,则另一种颜色的百分比会自动调整,以便总和达到 100%。例如,无论我们为 blue 指定 90% 还是为 red 指定 10%,结果都是相同的——一种主要为蓝色并带有一丝红色的颜色。

css
/* Both these will produce the same resultant color */
color-mix(in srgb, blue 90%, red)
color-mix(in srgb, blue, red 10%)

如果两种颜色的百分比之和 **小于** 100%,则 color-mix() 的行为略有不同:该和将被保存为 alpha 倍数,并且使用此倍数缩放这两种颜色,使其总和达到 100%。 (请参阅规范中的 百分比归一化 部分以获取一些示例)。

尽管下面的两个 color-mix() 函数混合了相同数量的每种颜色,但第二个函数(其中百分比之和为 40%)产生了相同的颜色,但 alpha 值为 0.4

css
/* Result: rgb(128 0 128) */
color-mix(in srgb, blue, red)

/* Result: rgb(128 0 128 / 0.4) */
color-mix(in srgb, blue 20%, red 20%)

使用 color-mix() 创建亮度和暗度变化

作为典型用例,我们通常需要创建品牌颜色的更亮或更暗的变体。为了实现这一点,我们可以使用 color-mix() 以不同的数量将白色或黑色混合到我们的基础颜色中。

下面的示例演示了如何将不同数量的 whiteblack 与基础颜色 blue 混合以创建其更亮和更暗的变体,展示了 color-mix() 在调整基础颜色强度方面的用途。

css
/* Initial base color */
.bg-blue {
  background-color: blue;
}

/* 50% blue, 50% white */
.bg-blue-light {
  background-color: color-mix(in srgb, blue, white);
}

/* 25% blue, 75% white */
.bg-blue-lighter {
  background-color: color-mix(in srgb, blue, white 75%);
}

/* 50% blue, 50% black */
.bg-blue-dark {
  background-color: color-mix(in srgb, blue, black);
}

/* 25% blue, 75% black */
.bg-blue-darker {
  background-color: color-mix(in srgb, blue, black 75%);
}

使用自定义属性重用颜色变体

通过将 color-mix() 值存储为自定义属性,我们可以在整个代码中重用它们。当我们想要创建品牌主色的更亮或更暗的变体时,这种方法很有用。

例如,下面的代码说明了如何使用 --brand 自定义属性创建品牌颜色变体。

css
:root {
  --brand: rgb(0 0 255);

  --brand-light: color-mix(in srgb, var(--brand), white);
  --brand-lighter: color-mix(in srgb, var(--brand), white 75%);
  --brand-dark: color-mix(in srgb, var(--brand), black);
  --brand-darker: color-mix(in srgb, var(--brand), black 75%);
}

我们还可以通过混合 transparent 来创建不同不透明度的变体

css
:root {
  --brand: rgb(0 0 255);
  --brand-alpha-50: color-mix(in srgb, blue, transparent);
  --brand-alpha-75: color-mix(in srgb, blue 75%, transparent);
}

Una Kravets 的文章 使用 color-mix() 创建不透明度变体 进一步解释了这一点。

示例:使用 color-mix() 自定义属性样式化按钮变体

让我们将 color-mix() 自定义属性应用于一个实际案例:样式化一个简单的按钮。首先,我们为我们的主要基础颜色和辅助颜色定义自定义属性。作为奖励,我们使用 color-mix() 为我们的辅助颜色混合基础颜色和 pink

css
:root {
  --brand: rgb(0 0 255);
  --brand-light: color-mix(in srgb, blue, white);

  --secondary: color-mix(in srgb, var(--brand), pink);
  --secondary-light: color-mix(in srgb, var(--secondary), white);
}

接下来,我们使用这些颜色应用于主要和辅助按钮变体,并使用更浅的颜色变体作为悬停状态。

css
button {
  background-color: var(--brand);
  color: white;
}

button:where(:hover, :focus) {
  background-color: var(--brand-light);
}

button.secondary {
  background-color: var(--secondary);
}

button.secondary:where(:hover, :focus) {
  background-color: var(--secondary-light);
}

我们不仅限于仅在根级别定义自定义属性。例如,我们可以为组件的基础颜色设置一个自定义属性,并使用 color-mix() 在组件的样式中创建此基础颜色的变体。对于辅助组件变体,我们可以简单地应用不同的基础颜色。如下所示。

css
.card {
  --color: blue;
  background: color-mix(in srgb, var(--color), white 80%);
  border-top: 5px solid var(--color);
  padding: 1rem;
}

.secondary {
  --color: deeppink;
}

这是一个将此概念应用于各种 UI 组件的演示。

使用 color-mix() 创建暖色和冷色变化

虽然创建现有颜色的更亮或更暗的变体是 color-mix() 的常见用例,但除此之外,我们还可以通过将更暖或更冷的颜色混合到我们的原始调色板中来创建暖色和冷色变体。

在这里,我们定义了一个初始调色板(从 Coolors 获取的颜色),以及我们想要混合以使用自定义属性创建暖色和冷色变体的颜色。

css
:root {
  --yellow: rgb(221 215 141);
  --peach: rgb(220 191 133);
  --chocolate: rgb(139 99 92);
  --khaki: rgb(96 89 77);
  --grey: rgb(147 162 155);

  --mix-warm: red;
  --mix-cool: blue;
}

.palette > div {
  --color: var(--yellow);

  &:nth-child(2) {
    --color: var(--peach);
  }

  &:nth-child(3) {
    --color: var(--chocolate);
  }

  &:nth-child(4) {
    --color: var(--khaki);
  }

  &:nth-child(5) {
    --color: var(--grey);
  }
}

然后我们使用自定义属性将混合到原始基础颜色中的第二种颜色应用于整个调色板,并指定数量。我们还指定了默认值,因此,如果未为 --mix 提供值,则将使用原始基础颜色。

css
.palette > div {
  background: color-mix(
    in srgb,
    var(--color),
    var(--mix, var(--color)) var(--amount, 10%)
  );
}

这样,我们就可以混合不同的颜色并将其应用于整个调色板。

css
.cool {
  --mix: var(--mix-cool);
}

.cool--20 {
  --amount: 20%;
}

.warm {
  --mix: var(--mix-warm);
}

.warm--20 {
  --amount: 20%;
}

color-mix() 中指定颜色插值空间

在前面的部分中,我们使用 srgb(标准红绿蓝)作为 颜色插值方法。通过修改我们用于插值的颜色空间,我们可以极大地改变结果。颜色空间是一个复杂的话题,超出了本文的范围,但在决定在 color-mix() 中使用哪种颜色空间时,值得注意一些颜色空间的优缺点。

探索颜色空间选项

颜色插值决定了一种颜色如何过渡到另一种颜色。一个很好的可视化方法是使用渐变,就像 Adam Argyle 在 颜色插值 部分中所做的那样,该部分深入探讨了颜色空间和色域。

经典的 RGB 插值会导致中心插值区域(渐变的中间区域)出现浑浊的颜色,而使用 lchoklch 时,颜色会保持鲜艳。如下图所示,当应用于前面示例中的暖色和冷色调色板时,结果截然不同。

The same color mixes produce different results when we compare sRGB (left) and OKLCH (right) color spaces

srgbhsl 不同,oklchoklab 都是感知均匀的。用 Lea Verou 的话来说,这意味着

坐标的相同数值变化会产生相同的感知颜色差异

因此,在使用 color-mix() 进行颜色插值时,我倾向于更喜欢 oklchoklab 颜色空间——但选择权在你!

了解较短和较长的插值路径

在极坐标(或圆形)颜色空间(如 oklchoklabhsl)中,我们还可以选择颜色插值的方向。当我们以相等比例混合两种颜色时,结果色相角将在两种颜色的角度之间。

css
color-mix(in hsl, rgb(255 88 88), rgb(86 86 255));
color-mix(in hsl longer hue, rgb(255 88 88), rgb(86 86 255));

Comparing longer and shorter interpolation direction in the HSL color space. The shorter hue produces a pink-ish purple (left) and the longer hue produces a bright green (right).

在下面的框架中,尝试在不同的颜色空间中混合颜色以观察结果。

color-mix() 的浏览器支持

自 2023 年年中以来,所有现代浏览器都已支持 color-mix() 函数。与往常一样,请记住,并非所有用户都拥有最新的浏览器。确保每个人都能获得可用体验的一种方法是设置初始颜色值。不支持 color-mix() 的浏览器将忽略第二个声明。

css
div {
  /* First declaration is fallback for browsers that do not support color-mix() */
  background: rgb(150 0 255);
  background: color-mix(in srgb, blue, red);
}

或者,我们可以使用特性查询来检测浏览器是否支持 color-mix() 并相应地提供样式

css
.card {
  background: lightblue;
}

@supports (color-mix(in srgb, blue, white)) {
  .card {
    --color: blue;
    background: color-mix(in srgb, var(--color), white 80%);
    border-top: 5px solid var(--color);
  }
}

如果你使用 PostCSS,则有一个 PostCSS 插件postcss-preset-env 捆绑在一起。此插件允许你编写 color-mix() 函数,而无需担心浏览器支持,因为你的代码在构建时会自动转换为跨浏览器兼容的 CSS。例如

css
.some-element {
  background-color: color-mix(in srgb, red, blue);
}

变为

css
.some-element {
  background-color: rgb(128 0 128);
}

值得注意的是,这仅适用于静态值,而不适用于在 JavaScript 中设置的自定义属性或动态值。

颜色插值资源

如果你有兴趣更深入地了解 Web 上的颜色空间和颜色插值,以下是一些文章来帮助你理解它们

总结

我们已经了解了如何使用 color-mix() 创建颜色变体,以及如何在项目和 desain 系统中将该函数与自定义属性结合使用。由于浏览器支持已经相当不错,我们可以期待 Web 上颜色处理的新时代!

关注 MDN

订阅 MDN 时事通讯,不错过任何关于最新 Web 开发趋势、技巧和最佳实践的更新。