前言

大多数开发者处理颜色时,就是从设计稿里复制粘贴一个色值,然后就完事了。

但是!CSS 颜色在过去几年里发生了很多变化!

不仅从 Web 十六进制代码演变成了 hsl() 函数,而且就连你熟知的 rgb() 函数也跟以前不同了!

不信我们接着往下看。

现代 CSS 写法

你不需要添加 a了

以前,我们用 rgb() 填写普通的 RGB 颜色值,要想改变不透明度,就必须使用 rgba()

.red {
  color: rgb(255, 0, 0);
}

.red-50 {
  color: rgba(255, 0, 0, 0.5);
}

现在,你可以直接添加第 4 个通道了:

.red {
  color: rgb(255, 0, 0);
}

.red-50 {
  color: rgb(255, 0, 0, 0.5);
}

而且,不用担心浏览器兼容问题,只要你不用支持 IE。

空格分隔语法

除此之外,逗号现在也被视为老语法了,对于新的颜色函数,我们甚至不能再使用逗号,只能使用新的空格语法。

.red {
  color: rgb(255 0 0);
}

.blue {
  color: hsl(226 100% 50%);
}

不过,使用空格分隔语法时要注意:你不能为 alpha 通道添加第四个值。

换句话说,这样写 color: rgb(255 0 0 0.5) 是不可以的。

如果你要添加第 4 个值,你需要在字母值之前使用一个正斜杠:

.red {
  color: rgb(255 0 0);
}

/* 50% 透明 */
.red-50 {
  color: rgb(255 0 0 / 0.5);
}

.hsl-red-50 {
  color: hsl(0 100% 50% / 0.5);
}

为什么要这么做呢?

因为 CSS 新增了很多颜色函数,统一用斜杠可以快速区分“颜色值”和“透明度”,看代码时一目了然。

hsl() 也变了

单位现在可选了:

.red {
  color: hsl(0deg 100% 50%);
}

.also-red {
  color: hsl(0deg 100 50);
}

.another-red {
  color: hsl(0 100% 50%);
}

.this-is-red-too {
  color: hsl(0 100 50);
}
最好还是保留百分号,因为 VS Code 只有带百分号的才会显示颜色预览。

相对颜色

相对颜色是什么?

简单说就是基于现有颜色生成新颜色。

基础用法

.rgb-red {
  color: rgb(from #ff0000 r g b);
}

这行代码的意思是:

基于 #ff0000 这个颜色,提取它的红(r)、绿(g)、蓝(b)值,然后用这些值创建一个新颜色。

结果其实就是 rgb(255 0 0)

看起来很傻对吧?但重点是下面这个:

.rgb-red {
  color: rgb(from #ff0000 r g b);
}

/* 轻松创建 50% 透明度版本 */
.rgb-red-50 {
  color: rgb(from #ff0000 r g b / 0.5);
}

最实用的场景:处理 CSS 变量

以前你想让一个 CSS 变量颜色变透明,得这样:

:root {
  --color-primary: #2563eb;
  --color-primary-transparent: rgba(37, 99, 235, 0.75); /* 手动转换,麻烦! */
}

现在呢,你可以直接这样写:

:root {
  --color-primary: #2563eb;
}

.semi-transparent-primary-background {
  /* 直接基于变量创建透明版本 */
  background-color: hsl(from var(--color-primary) h s l / 0.75);
}

就像你有一张照片,以前想调整透明度要重新处理一遍,现在滤镜一点就搞定。

快速生成配色方案

我们可以快速创建基础颜色的浅色和深色版本:

:root {
  --base: hsl(217 73% 50%);
  --base-light: hsl(from var(--base) h s 75%); /* 调亮 */
  --base-dark: hsl(from var(--base) h s 25%); /* 调暗 */
}

比如实现一个 Toast,它可能基于一种基础颜色,然后创建一种较深的颜色用于文本,一种较浅的颜色用于背景,还需要一个不透明度较低的颜色用于阴影。

以前要 4 个值,现在直接 1 个值搞定:

.toast {
  --toast-color: #222;

  /* 深色文字 */
  color: hsl(from var(--toast-color) h s 15%);

  /* 原色边框 */
  border: 2px solid var(--toast-color);

  /* 浅色背景 */
  background: hsl(from var(--toast-color) h s 90%);

  /* 半透明阴影 */
  box-shadow: 0 12px 12px -8px hsl(from var(--toast-color) h s l / 0.325);
}

此时换颜色也超简单:

[data-toast="info"] {
  --toast-color: #0362fc; /* 蓝色 */
}

[data-toast="error"] {
  --toast-color: hsl(0 100% 50%); /* 红色 */
}

一个变量搞定所有颜色变体,优雅!十分优雅!

浅暗主题切换

以前的痛苦

不知道你是否实现过网站的浅色和深色主题:

:root {
  /* 默认浅色主题 */
  --text-heading: #000;
  --text-body: #212121;
  --surface: #efefef;

  @media (prefers-color-scheme: dark) {
    /* 暗色主题 - 第一遍 */
    --text-heading: #fff;
    --text-body: #efefef;
    --surface: #212121;
  }
}

.dark-theme {
  /* 暗色主题 - 又写一遍! */
  --text-heading: #fff;
  --text-body: #efefef;
  --surface: #212121;
}

同样的颜色写两遍,一个给媒体查询(自动切换),一个给切换按钮。

改一次要改两个地方,烦死了!

现在的解决方案:light-dark()

:root {
  /* 跟随系统偏好 */
  color-scheme: light dark;

  /* 一次定义,自动切换 */
  --text-heading: light-dark(#000, #fff);
  --text-body: light-dark(#212121, #efefef);
  --surface: light-dark(#efefef, #212121);
}

light-dark(浅色, 暗色) 就这么简单!系统是浅色就用第一个,暗色就用第二个。

添加手动切换按钮

如果让用户手动切换主题:

:root {
  /* 默认跟随系统 */
  color-scheme: light dark;
  --text-heading: light-dark(#000, #fff);
  --text-body: light-dark(#212121, #efefef);
  --surface: light-dark(#efefef, #212121);
}

/* 用户选了浅色,锁定 */
html[data-theme="light"] {
  color-scheme: light;
}

/* 用户选了暗色,锁定 */
html[data-theme="dark"] {
  color-scheme: dark;
}

组件精细控制

如果某个区域必须保持固定颜色,比如某个背景图上必须是白字:

.hero {
  /* 不管全局主题怎么变,这里永远是浅色主题 */
  color-scheme: light;
  background: url("light-background.webp");
}

渐变优化

以前的痛苦

让我们直接看例子,这是一个从蓝到红的渐变:

.gradient {
  --color-1: hsl(219 76 41); /* 蓝色 */
  --color-2: hsl(357 68 53); /* 红色 */
  background: linear-gradient(90deg, var(--color-1), var(--color-2));
}

以前你只能手动加个中间色:

.better {
  --middle: hsl(271 52 41); /* 紫色 */
  background: linear-gradient(90deg, var(--color-1), var(--middle), /* 加一个中间色 */ var(--color-2));
}

是不是好看多了:

但是麻烦呀!我甚至可能需要添加两到三个额外的色阶,以确保它与原设计完全相同。

现在的解决办法:指定颜色空间

现在你可以轻松解决,只需要指定一个颜色空间:

.better {
  /* 用 oklch 颜色空间插值,中间色更鲜艳 */
  background: linear-gradient(in oklch 90deg, var(--color-1), var(--color-2));
}

就像拍照时选滤镜,不同的颜色空间会产生不同的中间色效果。oklch 在大多数情况下效果最好。

看下效果对比:

唯一的真正问题是,不同的颜色空间可能更适合不同的渐变,所以有时确实需要花点时间摸索。

可选的颜色空间有:

  • oklch/lch
  • oklab/lab
  • hwb
  • xyz
  • 不指定默认是srgb

实现彩虹渐变

以前做彩虹要指定每一个颜色。

现在只需要:

.rainbow {
  /* 从红色绕一整圈再回到红色,走长路径 */
  background: linear-gradient(in hsl longer hue 90deg, red, red);
}

longer hue 的意思是“走远路”,这样就能经过所有颜色了。

实现效果如下:

超宽色域——当客户就要那个色

有时候客户会拿着他们的 Logo 说:“我就要这个色,一模一样的!”

问题是,普通的 hex、rgb() 和 hsl() 用的是 sRGB 色域,能表示的颜色有限。

这就像以前电视只能显示几百种颜色,然而现在的手机屏幕能显示几百万种。

解决方案:display-p3

为了满足客户的需求,你可以使用 color颜色函数,并使用 display-p3 色域。

.vibrant-green {
  /* 使用 display-p3 色域,颜色更鲜艳 */
  color: color(display-p3 0 1 0);
}

如果浏览器不支持,会自动回退到能显示的最接近颜色,不会出错。

你可以在 Chrome 的开发者工具查看这种颜色:

点击色块,它会显示你选择的颜色,但同时也会显示使用 sRGB 色域的显示器的色域限制。

建议: 除非客户真的非常在意那个特定颜色,一般用不着。但知道有这个方案总是好的。

总结

现在你可以更轻松地写代码了:

  • 少打字 - 不用区分 rgba 和 rgb,用空格代替逗号
  • 少定义变量 - 用相对颜色基于一个颜色生成多个变体
  • 少写重复代码 - light-dark() 一次定义两套主题
  • 更好的渐变 - 指定颜色空间让中间色更漂亮
  • 更精确的颜色 - 需要时可以用更宽的色域

最重要的是: 这些特性浏览器支持都很好了(除了 IE,但谁还在乎 IE 呢?)

转载说明

本文转自:掘金用户 冴羽 

原文链接:《 现代 CSS 颜色使用指南 》