HSL 颜色模型
(色相、饱和度、亮度)为开发者提供了一种比 RGB 更直观的 CSS 颜色书写方式。你不再需要猜测红、绿、蓝三个通道的组合才能得到想要的颜色,HSL 让你用大脑本来思考颜色的方式来描述它:先选一个颜色,再决定它有多鲜艳,最后决定它有多亮或多暗。这种思维方式的转变,正是越来越多开发者放弃
rgb()
转而使用
hsl()
的原因。
目录
RGB 与 HSL 的核心区别
RGB 通过混合红、绿、蓝三个光通道来定义颜色,每个通道的取值范围是 0 到 255。问题在于,这是屏幕渲染像素的方式,而不是人类感知颜色的方式。如果你想在 RGB 中让一个蓝色稍微亮一点,你必须以恰当的比例同时调整三个通道的值,没有一个单独的旋钮可以转动。
HSL 则直接对应人们在日常对话中描述颜色的方式:
- 色相(Hue) - 颜色本身,以 360 度色轮上的角度表示。红色是 0 度,绿色是 120 度,蓝色是 240 度。
- 饱和度(Saturation) - 颜色的鲜艳程度,从 0%(纯灰色)到 100%(完全鲜艳)。
- 亮度(Lightness) - 颜色的明暗程度,从 0%(黑色)到 100%(白色),50% 为"纯正"颜色。
下面是同一种中等蓝色用两种格式表示的对比:
/* RGB - what do these numbers even mean at a glance? */
color: rgb(70, 130, 180);
/* HSL - instantly readable: blue hue, moderate saturation, medium lightness */
color: hsl(207, 44%, 49%);
两者都能呈现钢蓝色,但只有 HSL 版本让你一眼就能读懂信息。色相是 207(偏蓝),饱和度适中为 44%,亮度居中为 49%。不用打开取色器,你就能对这个颜色有清晰的认知。
| 属性 | RGB | HSL |
|---|---|---|
| 人类可读性 | 很少 | 是 |
| 调亮/调暗颜色 | 需调整 3 个值 | 只调整亮度 |
| 改变饱和度 | 需重新计算全部 3 个值 | 只调整饱和度 |
| 创建色板 | 反复试错 | 按角度旋转色相 |
| 配合 CSS 变量进行主题化 | 冗长繁琐 | 简洁且可预测 |
HSL 在 CSS 中的语法
CSS 的
hsl()
函数自 CSS3 起就已得到支持。现代语法(CSS Color Level 4)还允许在
hsl()
内部直接传入第四个 alpha 参数,这使得
hsla()
在很大程度上已经多余:
/* Classic syntax */
color: hsl(207, 44%, 49%);
/* With alpha (transparency) - old way */
color: hsla(207, 44%, 49%, 0.8);
/* Modern CSS Color Level 4 syntax - commas optional, alpha with slash */
color: hsl(207 44% 49%);
color: hsl(207 44% 49% / 0.8);
color: hsl(207 44% 49% / 80%);
所有主流浏览器都支持这两种语法。无逗号版本是 CSS 的发展方向,但带逗号的版本在包括 Internet Explorer 9+ 在内的所有浏览器中均可使用。
为什么 HSL 更适合 UI 开发
HSL 的真正威力在你需要生成颜色变体的那一刻显现出来,而这在 UI 开发中几乎无处不在。
几秒内构建完整色板
假设你的品牌色是
hsl(140, 70%, 45%)
这样一个鲜艳的绿色。你需要一个悬停状态、一个禁用状态和一个浅色背景色调。在 HSL 中,每次只需改动一个值:
--color-base: hsl(140, 70%, 45%); /* base green */
--color-hover: hsl(140, 70%, 38%); /* darker - just lower lightness */
--color-disabled: hsl(140, 20%, 65%); /* washed out - lower saturation */
--color-tint: hsl(140, 70%, 92%); /* very light background tint */
试试在不打开取色器的情况下用 RGB 做到同样的效果。那真的很难,因为没有一个独立的轴可以单独调整。
类似色与互补色
由于色相是色轮上的角度,生成和谐的色板就是简单的数学运算。类似色之间相差 30 度以内,互补色则相差 180 度:
--primary: hsl(210, 80%, 50%); /* blue */
--analogous-1: hsl(180, 80%, 50%); /* cyan - 30 degrees left */
--analogous-2: hsl(240, 80%, 50%); /* purple - 30 degrees right */
--complementary: hsl(30, 80%, 50%); /* orange - 180 degrees opposite */
饱和度和亮度保持不变,因此这些颜色看起来像一个和谐的家族。这正是 Tailwind CSS 和 Material Design 等设计系统以编程方式生成色阶的原理。
实用 CSS 示例
将 HSL 各分量拆分为 CSS 自定义属性
最强大的 HSL 使用模式之一,是将三个值分别存入独立的 CSS 自定义属性。这样你可以在任何地方重新组合它们,并随时调整单个通道:
:root {
--brand-h: 210;
--brand-s: 80%;
--brand-l: 50%;
--brand-color: hsl(var(--brand-h), var(--brand-s), var(--brand-l));
}
.button {
background-color: var(--brand-color);
}
.button:hover {
/* Just override lightness - no need to redefine the whole color */
background-color: hsl(var(--brand-h), var(--brand-s), 40%);
}
.button:disabled {
background-color: hsl(var(--brand-h), 20%, 70%);
}
hsl()
内部使用 CSS 自定义属性时,需要加逗号:
hsl(var(--h), var(--s), var(--l))
。目前并非所有浏览器都支持在
hsl()
内部使用
var()
的无逗号现代语法。
生成完整的色调色阶
设计系统通常需要一种颜色的 9 到 10 个色阶(类似 Tailwind 的 50 到 950 色阶)。使用 HSL,你可以在保持色相和饱和度不变的情况下,以固定间隔步进亮度来生成整套色阶:
:root {
--blue-50: hsl(210, 80%, 95%);
--blue-100: hsl(210, 80%, 87%);
--blue-200: hsl(210, 80%, 76%);
--blue-300: hsl(210, 80%, 65%);
--blue-400: hsl(210, 80%, 55%);
--blue-500: hsl(210, 80%, 50%); /* base */
--blue-600: hsl(210, 80%, 43%);
--blue-700: hsl(210, 80%, 36%);
--blue-800: hsl(210, 80%, 26%);
--blue-900: hsl(210, 80%, 16%);
}
如果你想深入了解各种颜色格式之间的关系, HEX 与 RGB 转换指南 全面介绍了 CSS 颜色格式的完整图景,包括何时该选用哪种格式。
悬停状态、主题化与暗色模式
悬停与焦点状态
HSL 让悬停状态的实现变得非常简单。你不需要单独定义一个全新的颜色,只需调整亮度即可:
.btn-primary {
background: hsl(210, 80%, 50%);
transition: background 0.2s ease;
}
.btn-primary:hover { background: hsl(210, 80%, 43%); }
.btn-primary:active { background: hsl(210, 80%, 36%); }
.btn-primary:focus-visible {
outline: 3px solid hsl(210, 80%, 70%);
}
每个状态都与基础颜色清晰关联。阅读这段代码的同事能立刻理解各状态之间的关系。
使用 HSL 变量实现主题化
HSL 是现代 CSS 主题化的核心。只需将色相暴露为一个变量,就能让用户或管理员通过修改一个数字来改变整个应用的配色方案:
/* Default theme: blue */
:root {
--theme-hue: 210;
}
/* Green theme - just swap the hue */
[data-theme="green"] {
--theme-hue: 140;
}
/* Purple theme */
[data-theme="purple"] {
--theme-hue: 270;
}
/* All components use the same hue variable */
.button { background: hsl(var(--theme-hue), 75%, 50%); }
.link { color: hsl(var(--theme-hue), 75%, 40%); }
.badge { background: hsl(var(--theme-hue), 75%, 92%); color: hsl(var(--theme-hue), 75%, 25%); }
.focus-ring { outline-color: hsl(var(--theme-hue), 75%, 65%); }
用 HSL 实现暗色模式
暗色模式正是 HSL 大放异彩的场景。你不需要维护两套完全独立的色板,只需在同一色相下翻转亮度值:
:root {
--bg: hsl(210, 20%, 98%); /* near-white background */
--text: hsl(210, 20%, 15%); /* near-black text */
--card: hsl(210, 20%, 93%); /* slightly darker card */
}
@media (prefers-color-scheme: dark) {
:root {
--bg: hsl(210, 20%, 10%); /* flip: near-black background */
--text: hsl(210, 20%, 90%); /* flip: near-white text */
--card: hsl(210, 20%, 15%); /* slightly lighter card in dark */
}
}
注意,色相(210)和饱和度(20%)始终不变,只有亮度发生了翻转。这使暗色模式看起来像是同一套设计的反转版本,这正是优秀暗色模式应该呈现的效果。
浏览器支持与兼容性
HSL 的浏览器支持非常出色。
hsl()
函数自以下版本起就已可用:
- Chrome 1(2008 年)
- Firefox 1(2004 年)
- Safari 3.1(2008 年)
- Internet Explorer 9(2011 年)
- Edge 12(2015 年)
现代无逗号语法(
hsl(210 80% 50%)
)和斜杠 alpha 语法(
hsl(210 80% 50% / 0.5)
)属于
CSS Color Level 4 规范
,自 2023 年起已在所有现代浏览器中获得支持。如果你仍需支持 IE11(目前已较为罕见),请坚持使用带逗号的语法。
hsl()
在用户可能使用的所有浏览器中均可正常运行。现代空格分隔语法在 Chrome 90+、Firefox 89+ 和 Safari 14.1+ 中均可使用,覆盖全球超过 95% 的浏览器用户。
你随时可以使用 取色器工具 在 HSL、RGB 和 HEX 格式之间相互转换,方便交叉核对颜色值,或将颜色交付给只接受特定格式的工具。
HSL 与 OKLCH - 下一步是什么?
如果你想在 HSL 之上更进一步, OKLCH 值得了解。它是一个感知均匀的色彩空间,意味着亮度或色度上相同的数值步长,在人眼看来也是相同的视觉变化,而 HSL 并不能完全保证这一点。
HSL 的问题在于,两个亮度值相同的颜色在感知亮度上可能差异很大。例如,
hsl(60, 100%, 50%)
(黄色)看起来比
hsl(240, 100%, 50%)
(蓝色)亮得多,尽管两者的亮度都是 50%。OKLCH 纠正了这个问题。
/* HSL - same lightness, different perceived brightness */
color: hsl(60, 100%, 50%); /* yellow - looks very bright */
color: hsl(240, 100%, 50%); /* blue - looks much darker */
/* OKLCH - same lightness, actually looks the same to the eye */
color: oklch(0.75 0.18 90); /* yellow-ish */
color: oklch(0.75 0.18 260); /* blue-ish - genuinely similar perceived brightness */
OKLCH 还支持
广色域颜色
(P3 显示色域),超出了 HSL 所局限的 sRGB 范围。
oklch()
在 2024 年的浏览器支持已相当稳定:Chrome 111+、Firefox 113+、Safari 15.4+。
对于目前大多数项目而言,HSL 是最实用的选择:可读性强、易于维护、兼容性全面,相比 RGB 有着巨大的提升。当你在构建需要数学上一致的对比度比例的设计系统,或者需要针对广色域显示器时,OKLCH 才是更合适的选择。
如果你想在构建色板时以可视化方式探索颜色关系, 颜色探索工具 可以让你实时调试 HSL 值,观察色相旋转、饱和度变化和亮度调整如何相互影响。
告别猜测,精准选取 HSL 颜色
我们的取色器让你通过可视化方式调整色相、饱和度和亮度,然后直接将 HSL 颜色模型的输出值复制到你的 CSS 中,无需任何心算。
立即试用取色器 →
你不需要做全面重写,但对于新写的颜色定义,尤其是在使用 CSS 自定义属性时,切换到 HSL 是值得的。收益最大的场景包括:构建或维护设计系统、创建悬停状态,以及实现暗色模式。在同一个项目中混用 RGB 和 HSL 是完全合法的 CSS 写法,浏览器处理两者时不存在任何性能差异。
没有任何实质性的性能差异。浏览器在解析时会将所有 CSS 颜色格式转换为内部表示,因此无论你写的是
hsl(207, 44%, 49%)
还是
rgb(70, 130, 180)
,渲染引擎在完成初始解析后对它们的处理方式完全相同。选择哪种格式完全取决于开发体验和可维护性,与运行时性能无关。
可以,而且这是 HSL 在 JavaScript 中最强大的应用场景之一。你可以将色相、饱和度和亮度分别存储为独立的数值,然后动态构建颜色字符串:
element.style.color = `hsl(${hue}, ${saturation}%, ${lightness}%)`
。这使得动画、主题切换和交互式颜色控件的实现比使用 RGB 通道简单得多,因为后者需要转换计算才能达到同样的效果。
HSL(色相、饱和度、亮度)和 HSB/HSV(色相、饱和度、明度/色值)是不同的颜色模型,尽管它们共享色相轴。在 HSL 中,50% 的亮度对应纯正的完全饱和颜色;在 HSB 中,100% 的明度才对应纯正颜色。对于相同的饱和度和明度/亮度值,两种模型会产生不同的结果。CSS 使用的是 HSL,而 HSB/HSV 常见于 Photoshop 和 Figma 等设计工具,并非 CSS 原生格式。
从 HEX 到 HSL 的转换需要经过一个中间的 RGB 步骤:首先将 HEX 的每对十六进制数转换为 RGB 值(0-255),将其归一化到 0-1 范围,再套用 HSL 转换公式。实际上,大多数开发者会使用取色器工具、Figma 等设计工具,或浏览器开发者工具中的取色器来即时完成转换,而不是手动计算。浏览器开发者工具允许你点击任意颜色色块,在 HEX、RGB 和 HSL 表示之间循环切换。
是的,OKLCH 在无障碍访问工作中更可靠,因为它是感知均匀的,其亮度通道上相同的数值步长对应人眼感知到的相同亮度变化。这使得构建对比度在不同色相下都可预测的色板更加容易。使用 HSL 时,50% 亮度的黄色看起来比 50% 亮度的蓝色亮得多,这可能在无障碍访问上带来意外问题。对于符合 WCAG 对比度规范的场景,OKLCH 在构建无障碍色阶时能给你更可预测的结果。