教程

CSS color-mix():在浏览器中原生混合颜色

阅读约 3 分钟

多年来,在 CSS 中生成颜色变体要么需要手动硬编码每一个深浅值,要么借助 JavaScript 和 chroma.js 或 tinycolor2 等颜色处理库。CSS color-mix() 函数改变了这一切。它是一个原生 CSS 函数,现已在所有主流浏览器中获得支持,可以按指定比例混合两种颜色——直接在你的样式表中,无需 JavaScript,无需预处理器。

本文全面介绍了 color-mix() 的语法,解释了为什么色彩空间的选择至关重要,逐一梳理了真正实用的场景,并涵盖了浏览器支持和回退策略。

什么是 color-mix()?

color-mix() 是 CSS Color Level 5 中的函数,接受两个颜色值和一个混合比例,返回混合后的颜色。它相当于 CSS 版的颜料调色,但有一个关键的额外参数:混合所在的色彩空间

最简单的用例是从基础颜色创建色调和深浅变体。无需为品牌蓝色的每个深浅变体逐一定义,可以动态推导:

:root {
  --brand-blue: #2563EB;

  /* 色调——与白色混合 */
  --brand-blue-light:   color-mix(in oklch, var(--brand-blue) 60%, white);
  --brand-blue-lighter: color-mix(in oklch, var(--brand-blue) 30%, white);

  /* 暗调——与黑色混合 */
  --brand-blue-dark:    color-mix(in oklch, var(--brand-blue) 80%, black);
  --brand-blue-darker:  color-mix(in oklch, var(--brand-blue) 50%, black);
}

基础蓝色 #2563EB 只定义一次,色调和暗调从中推导而来。品牌色变更后,所有变体会自动更新。

语法与参数

color-mix() 的完整语法为:

color-mix(in <color-space>, <color1> [<percentage>]?, <color2> [<percentage>]?)

色彩空间参数

第一个参数 in <color-space> 是必需的,用于指定混合发生的位置。有效选项包括:

  • srgb — 在标准 RGB 中混合(最熟悉,但容易产生浑浊的中间点)
  • hsl — 沿 HSL 圆柱混合
  • hwb — 在 HWB(色相、白度、黑度)中混合
  • lab — 在 CIE Lab(感知均匀)中混合
  • oklab — 在 Oklab(均匀性优于 CIE Lab)中混合
  • lch — 在 LCH(圆柱坐标 CIE Lab)中混合
  • oklch — 在 OKLCH(圆柱坐标 Oklab,大多数场景推荐)中混合
  • display-p3 — 在 Display P3 宽色域中混合
  • xyz — 在 CIE XYZ 中混合

对于大多数设计工作,oklch 产生最为符合感知的自然结果。下一节将详细说明。

颜色参数

两个颜色值可以是任何有效的 CSS 颜色:十六进制代码、rgb()hsl()oklch()、命名颜色或 CSS 自定义属性。每个颜色可以附带一个百分比,表示该颜色对混合结果的贡献量:

/* 50/50 混合(未指定百分比时的默认值) */
color-mix(in oklch, #FF5733, #3498DB)

/* 70% 第一种颜色,30% 第二种颜色 */
color-mix(in oklch, #FF5733 70%, #3498DB 30%)

/* 80% 第一种颜色——第二种颜色百分比推算为 20% */
color-mix(in oklch, #FF5733 80%, #3498DB)

如果百分比之和小于 100%,结果为部分透明。如果超过 100%,则百分比会被归一化。如果两者均未指定百分比,则为 50/50 混合。

/* 以下等价 */
color-mix(in srgb, blue, red)
color-mix(in srgb, blue 50%, red 50%)
color-mix(in srgb, blue 50%, red)

在不同色彩空间中混合

色彩空间的选择不仅仅是外观上的差异——它从根本上改变了产生的颜色。相同的两种颜色在不同空间中混合会产生截然不同的结果。

在 sRGB 中混合

sRGB 混合通过对红、绿、蓝通道进行线性插值来实现。对于许多颜色对,结果尚可接受,但互补色(色轮对侧的颜色)通常会产生去饱和的灰色中间点。

/* 在 sRGB 中混合橙红色和蓝色 */
color-mix(in srgb, #FF5733 50%, #3498DB 50%)
/* 结果:一种暗淡、略带棕色的紫色——接近 [#9A77B7] */

问题在于,互补色的 RGB 通道在求平均值时相互抵消。红色 [255, 87, 51] 和蓝色 [52, 152, 219] 的中间点为 [154, 120, 135]——一种去饱和的灰暗结果。

在 HSL 中混合

HSL 混合沿色相轮插值,可能引发另一个问题:色相旋转经过意料之外的中间颜色。

/* 橙色(色相约 15°)与蓝色(色相约 210°)在 HSL 中混合 */
color-mix(in hsl, #FF5733 50%, #3498DB 50%)
/* 色相插值经过约 112°——你会得到一种偏绿的中间色 */

当两种颜色在色相轮上相距较远时,HSL 中它们之间的最短路径可能会经过绿色或其他意料之外的色相。HSL 同样存在 hsl() 普遍面临的感知非均匀性问题——中间点的亮度看起来可能并不处于两个源亮度的正中间。

在 OKLCH 中混合

OKLCH 混合是大多数实际用例的推荐方式。它在感知均匀的空间中插值,意味着中间颜色看起来像自然的视觉中间点——鲜艳、均衡,没有意料之外的色相漂移。

/* 橙红色与蓝色在 OKLCH 中混合 */
color-mix(in oklch, #FF5733 50%, #3498DB 50%)
/* 结果:鲜艳的洋红紫色——感知上的正中间 */

#FF5733(温暖的橙红色)和 #3498DB(明亮的蓝色)在 OKLCH 中以 50/50 混合,产生一种充满活力、饱和度高的中间色。色相直接插值穿过紫色范围——正是当被要求"取橙色和蓝色的中间值"时设计师所期望的结果。

在 Oklab 中混合(色相较短/较长路径)

对于色相插值,你还可以指定沿色轮走较短或较长的弧:

/* 较短色相路径(默认) */
color-mix(in oklch shorter hue, red, blue)

/* 较长色相路径——绕色轮另一侧走 */
color-mix(in oklch longer hue, red, blue)

/* 递增色相 */
color-mix(in oklch increasing hue, red, blue)

当你刻意希望插值经过特定中间色相时,这种控制很有用——例如,希望渐变从红色经过黄色再到蓝色,而非经过紫色。

对比表

色彩空间 橙色 + 蓝色中间点 视觉特征
srgb 暗淡的灰紫色 去饱和、浑浊
hsl 偏绿色 意外色相偏移
lab 紫色,不那么鲜艳 比 sRGB 更自然
oklch 鲜艳的洋红紫色 感知最为自然

使用颜色转换器 探索你的特定颜色的 OKLCH 值,预测它们的混合效果。

实际用例

创建悬停和激活状态

最立竿见影的应用之一是无需为每个状态单独定义变量即可生成交互状态:

:root {
  --btn-bg: oklch(0.55 0.22 250);  /* 你的品牌蓝色 */
}

.btn {
  background-color: var(--btn-bg);
}

.btn:hover {
  /* 通过与黑色混合降低 15% 亮度 */
  background-color: color-mix(in oklch, var(--btn-bg) 85%, black);
}

.btn:active {
  /* 按下状态进一步加深 */
  background-color: color-mix(in oklch, var(--btn-bg) 70%, black);
}

.btn:disabled {
  /* 禁用状态:去饱和并变亮 */
  background-color: color-mix(in oklch, var(--btn-bg) 40%, white);
  opacity: 0.6;
}

这消除了在设计工具中单独选取三个深浅值并将其硬编码的需要。悬停和激活颜色从基础色数学推导而来,即使品牌色变更,这种关系也保持一致。

构建语义色阶

设计系统需要语义颜色——成功、警告、危险——这些颜色需要与品牌调色板协调。color-mix() 让你从品牌色推导这些颜色,而不是独立选取:

:root {
  --brand: oklch(0.55 0.22 250);  /* 品牌蓝色 */

  /* 成功:与纯绿色混合,保留一点品牌色的痕迹 */
  --success: color-mix(in oklch, oklch(0.65 0.22 145) 85%, var(--brand) 15%);

  /* 警告:纯琥珀色——这里不需要混合 */
  --warning: oklch(0.75 0.18 70);

  /* 危险:纯红色 */
  --danger: oklch(0.62 0.24 25);

  /* 每种状态的淡色背景 */
  --success-bg: color-mix(in oklch, var(--success) 12%, white);
  --warning-bg: color-mix(in oklch, var(--warning) 12%, white);
  --danger-bg:  color-mix(in oklch, var(--danger)  12%, white);
}

--success-bg 是成功绿色的非常淡的色调,自动推导而来。可将其用于通知横幅、提示框和表单字段错误状态。

动态生成完整深浅色阶

虽然色阶生成器 是创建精确感知步进的完整 50-950 色阶的正确工具,但 color-mix() 可以为需要少量变体的组件内联生成可用的近似值:

:root {
  --primary: oklch(0.58 0.20 250);

  --primary-50:  color-mix(in oklch, var(--primary) 8%,  white);
  --primary-100: color-mix(in oklch, var(--primary) 15%, white);
  --primary-200: color-mix(in oklch, var(--primary) 30%, white);
  --primary-300: color-mix(in oklch, var(--primary) 50%, white);
  --primary-400: color-mix(in oklch, var(--primary) 70%, white);
  --primary-500: var(--primary);
  --primary-600: color-mix(in oklch, var(--primary) 85%, black);
  --primary-700: color-mix(in oklch, var(--primary) 70%, black);
  --primary-800: color-mix(in oklch, var(--primary) 55%, black);
  --primary-900: color-mix(in oklch, var(--primary) 35%, black);
  --primary-950: color-mix(in oklch, var(--primary) 20%, black);
}

深色模式颜色推导

color-mix() 在系统性地推导深色模式表面颜色方面特别有价值:

@media (prefers-color-scheme: dark) {
  :root {
    --surface-base:     #09090b;
    --surface-elevated: color-mix(in oklch, var(--brand) 8%, #09090b);
    --surface-overlay:  color-mix(in oklch, var(--brand) 12%, #09090b);
  }
}

在深色表面中混入少量品牌色,会创造出微妙的色调——这是 Apple macOS 和许多现代设计系统使用的技术,使深色模式感觉比纯中性表面更有温度。

Alpha 通道行为

当百分比之和不足 100% 时,结果为部分透明。这可以刻意用来创建半透明变体:

/* 透明基础上 30% 的蓝色——相当于 30% 不透明度的 rgba */
color-mix(in srgb, blue 30%, transparent)

/* 同样有效:从另一侧命名透明度 */
color-mix(in oklch, #3B82F6 25%, transparent)

这替代了使用固定 Alpha 值的 rgba() 模式。优点在于,不透明度关系在混合百分比中一目了然,且你可以与任何颜色混合——不仅限于纯透明。

浏览器支持与回退策略

截至 2026 年,color-mix() 已在以下浏览器中完全支持:

  • Chrome/Edge:自版本 111(2023 年 3 月)起
  • Firefox:自版本 113(2023 年 5 月)起
  • Safari:自版本 16.2(2022 年 12 月)起

全球支持率超过 90%。无法看到 color-mix() 结果的用户是那些使用非常旧的移动浏览器或企业环境中浏览器版本被锁定的用户。

渐进增强回退

CSS 自定义属性声明遵循层叠规则。如果浏览器不支持 color-mix(),则使用前一行的回退值:

:root {
  /* 不支持 color-mix() 的浏览器的回退 */
  --btn-hover: #1D4ED8;

  /* 为现代浏览器覆盖为计算值 */
  --btn-hover: color-mix(in oklch, var(--btn-bg) 85%, black);
}

由于自定义属性重新声明了同一变量,这作为渐进增强模式可以正常工作。不支持 color-mix() 的浏览器将使用显式回退颜色;现代浏览器将使用推导值。

或者,使用 @supports

.btn:hover {
  background-color: #1D4ED8; /* 回退 */
}

@supports (color: color-mix(in oklch, red, blue)) {
  .btn:hover {
    background-color: color-mix(in oklch, var(--btn-bg) 85%, black);
  }
}

@supports 方式更为明确,当你最终放弃旧浏览器支持时也更易于清理。

核心要点

  • color-mix() 在 CSS 中原生混合两种颜色,消除了许多用例对 JavaScript 或预生成深浅色表的需求。
  • in <color-space> 参数控制混合发生的位置,并显著影响结果。大多数工作使用 oklch——它产生感知自然的中间点,没有浑浊的去饱和(sRGB 问题)或意外的色相偏移(HSL 问题)。
  • 最实用的场景包括:交互状态(悬停、激活、禁用)、语义颜色推导、深色模式表面色调处理,以及从单个基础变量生成小型深浅色阶。
  • 当百分比之和小于 100% 时,输出为部分透明——适用于无需 rgba() 即可创建不透明度变体的场景。
  • 浏览器支持涵盖 2023 年初以来的所有现代浏览器。对于较旧环境,在前一个 CSS 声明中提供硬编码回退值,或在 @supports 块中提供。
  • 在使用 color-mix() 之前,使用颜色转换器 将现有品牌颜色转换为 OKLCH,使用调色板生成器 在编写 CSS 之前查看和谐的颜色关系。

相关颜色

相关品牌

相关工具