CSS `relative-color-syntax` (提案):基于现有颜色动态生成新颜色

各位观众老爷,大家好!今天咱们来聊聊CSS的未来战士——relative-color-syntax,也就是“相对颜色语法”。这玩意儿如果真能普及开来,那CSS玩颜色就彻底支棱起来了!

啥是相对颜色语法?

简单来说,就是你可以基于一个已有的颜色,通过一些操作,动态地生成一个新的颜色。听起来是不是有点像Photoshop里的颜色调整?没错,就是那个味儿!

想想以前,你想把一个按钮的颜色稍微调亮一点,要么用Sass的lighten()函数,要么就得吭哧吭哧地手动改hex值。现在有了相对颜色语法,就可以直接在CSS里完成,代码更简洁,也更容易维护。

语法初探

相对颜色语法的基本结构是这样的:

color: color( <reference-color> calc(<channel-adjustment>));
  • <reference-color>:这是你要参考的颜色,可以是hex值、rgb值、hsl值等等,甚至是CSS变量!
  • calc(<channel-adjustment>):这是个关键!calc()函数里面是你对颜色通道(比如红、绿、蓝)的具体调整。

举个例子,假设你想让一个元素的背景色比它的文本颜色稍微亮一点:

p {
  color: rgb(100, 150, 200); /* 原始文本颜色 */
  background-color: color(currentColor calc(red() + 20) calc(green() + 20) calc(blue() + 20)); /* 背景色比文本颜色更亮 */
}

这里,currentColor表示当前元素的文本颜色,red()green()blue()分别提取出红、绿、蓝通道的值,然后用calc()函数给每个通道加上20,就得到了一个更亮的背景色。

颜色通道函数

上面例子里用到了red()green()blue()这些函数,它们就是用来提取颜色的各个通道值的。除了这三个,还有很多其他的通道函数,它们才是相对颜色语法的核心武器!

下面这张表列出了一些常用的通道函数:

函数 描述 示例
red() 获取颜色的红色通道值(0-255 或者 0%-100%) color(red 255) 将红色通道设置为 255
green() 获取颜色的绿色通道值(0-255 或者 0%-100%) color(green 100%) 将绿色通道设置为 100%
blue() 获取颜色的蓝色通道值(0-255 或者 0%-100%) color(blue calc(blue() * 0.5)) 将蓝色通道值减半
alpha() 获取颜色的透明度(0-1) color(alpha 0.5) 将透明度设置为 50%
hue() 获取颜色的色相(0-360 度) color(hue calc(hue() + 30deg)) 将色相旋转 30 度
saturation() 获取颜色的饱和度(0%-100%) color(saturation calc(saturation() * 1.2)) 将饱和度增加 20%
lightness() 获取颜色的亮度(0%-100%) color(lightness calc(lightness() + 10%)) 将亮度增加 10%
whiteness() 获取颜色的白度(0%-100%) (仅在 oklchoklab 颜色空间中使用) color(oklch lightness calc(lightness() + 5%) whiteness calc(whiteness() - 2%))oklch 颜色空间中,增加亮度并减少白度
blackness() 获取颜色的黑度(0%-100%) (仅在 oklchoklab 颜色空间中使用) color(oklch lightness calc(lightness() - 5%) blackness calc(blackness() + 2%))oklch 颜色空间中,减少亮度并增加黑度
lch() 获取颜色的 LCH 值(亮度、色度、色相) (仅在 lch 颜色空间中使用) color(lch lightness calc(lch() * 1.1) chroma calc(chroma() * 0.9))lch 颜色空间中,增加亮度并减少色度
oklch() 获取颜色的 OKLCH 值(感知均匀的亮度、色度、色相) color(oklch lightness calc(oklch() * 1.1) chroma calc(chroma() * 0.9))oklch 颜色空间中,增加亮度并减少色度
lab() 获取颜色的 LAB 值(亮度、a 分量、b 分量) (仅在 lab 颜色空间中使用) color(lab lightness calc(lab() * 1.1) a calc(a() * 0.9))lab 颜色空间中,增加亮度并减少 a 分量
oklab() 获取颜色的 OKLAB 值(感知均匀的亮度、a 分量、b 分量) color(oklab lightness calc(oklab() * 1.1) a calc(a() * 0.9))oklab 颜色空间中,增加亮度并减少 a 分量
cielab() 获取颜色的 CIELAB 值(亮度、a 分量、b 分量) (已弃用,建议使用lab()oklab()) color(lab lightness calc(lab() * 1.1) a calc(a() * 0.9))lab 颜色空间中,增加亮度并减少 a 分量
cielch() 获取颜色的 CIELCH 值(亮度、色度、色相) (已弃用,建议使用lch()oklch()) color(lch lightness calc(lch() * 1.1) chroma calc(chroma() * 0.9))lch 颜色空间中,增加亮度并减少色度
hwb() 获取颜色的 HWB 值(色相、白度、黑度) color(hwb hue calc(hue() + 10deg) whiteness calc(whiteness() + 5%) blackness calc(blackness() - 5%)) 色相旋转 10 度,增加白度并减少黑度
l() 获取颜色的亮度值(仅在 lchlab 颜色空间中使用,可以理解为 lightness() 的别名) color(lch l calc(l() + 10%) chroma calc(chroma() * 0.9))lch 颜色空间中,增加亮度并减少色度
a() 获取颜色的 a 分量值(仅在 lab 颜色空间中使用,表示颜色在绿-红轴上的位置) color(lab l calc(l() + 5%) a calc(a() - 10))lab 颜色空间中,增加亮度并减少 a 分量(颜色更偏向绿色)
b() 获取颜色的 b 分量值(仅在 lab 颜色空间中使用,表示颜色在蓝-黄轴上的位置) color(lab l calc(l() + 5%) b calc(b() + 10))lab 颜色空间中,增加亮度并增加 b 分量(颜色更偏向黄色)
c()chroma() 获取颜色的色度值(仅在 lch 颜色空间中使用,表示颜色的鲜艳程度) color(lch l calc(l() + 5%) c calc(c() * 0.8))lch 颜色空间中,增加亮度并减少色度(颜色更柔和)

颜色空间

注意到上面表格里提到了 oklchoklablchlab 等等,这些就是颜色空间。不同的颜色空间用不同的方式来描述颜色,它们各有优缺点。

  • RGB: 最常见的颜色空间,用红、绿、蓝三个通道来表示颜色。简单直接,但是不太符合人类对颜色的感知。
  • HSL: 用色相、饱和度、亮度来表示颜色。比RGB更符合人类直觉,但是亮度通道的感知并不均匀。
  • LCH 和 LAB: 基于CIE Lab颜色模型,旨在提供感知均匀的颜色空间。LCH 使用亮度(L)、色度(C)和色相(H),而 LAB 使用亮度(L)以及 a 和 b 分量(分别代表绿-红轴和蓝-黄轴)。
  • OKLCH 和 OKLAB: 被认为是目前感知最均匀的颜色空间。OKLCH 是 LCH 的改进版本,OKLAB 是 LAB 的改进版本。在颜色操作和渐变中能提供更自然和一致的结果。
  • HWB: 用色相、白度、黑度来表示颜色。更适合用于创建基于白或黑的颜色变体。

选择合适的颜色空间很重要,尤其是在做复杂的颜色调整时。oklchoklab 通常是更好的选择,因为它们能提供更平滑、更自然的颜色过渡。

实战演练

光说不练假把式,咱们来几个实际的例子:

1. 创建一个悬停效果:

button {
  background-color: #4CAF50; /* 原始颜色 */
}

button:hover {
  background-color: color(#4CAF50 lightness(calc(lightness() + 10%))); /* 悬停时亮度增加10% */
}

2. 创建一个阴影效果:

.shadow {
  background-color: #f0f0f0;
  box-shadow: 0 2px 5px color(#000 alpha(calc(alpha() * 0.2))); /* 黑色阴影,透明度为20% */
}

3. 基于CSS变量动态调整颜色:

:root {
  --primary-color: #2196F3;
}

.element {
  background-color: var(--primary-color);
  color: color(var(--primary-color) lightness(calc(lightness() + 60%))); /* 文本颜色比背景色更亮 */
}

4. 使用 oklch 创建更柔和的颜色变化:

.brand-color {
  background-color: oklch(60% 0.2 240); /* 定义一个品牌颜色 */
  color: color(oklch(60% 0.2 240) lightness(calc(lightness() * 0.8))); /* 基于品牌颜色,创建一个更暗的文本颜色 */
}

5. 实现主题切换

结合CSS自定义属性,相对颜色语法可以方便地实现主题切换。

:root {
  --base-color: #29abe2; /* Sky Blue */
}

body {
  background-color: var(--base-color);
  color: color(var(--base-color) lightness(calc(lightness() * 0.9)) saturation(calc(saturation() * 0.8))); /* 更暗更柔和的文字颜色 */
}

/* 深色主题 */
body.dark-theme {
  --base-color: #2c3e50; /* Midnight Blue */
}

在这个例子中,我们定义了一个 --base-color 变量作为基础颜色。然后,我们使用相对颜色语法基于这个基础颜色生成文字颜色,使其在任何主题下都与背景色形成良好的对比。通过切换 bodydark-theme 类,我们可以轻松地切换主题,而文字颜色会自动调整。

注意事项

  • 浏览器兼容性: 相对颜色语法还是个新东西,目前(2024年)的浏览器支持情况还不是特别好。在使用前,最好查一下Can I Use,并做好降级方案。
  • 性能: 复杂的颜色计算可能会影响性能。尽量避免在动画中使用过于复杂的相对颜色语法。
  • 可读性: 虽然相对颜色语法很强大,但是过度使用可能会降低代码的可读性。要适度使用,并添加必要的注释。
  • 单位: 确保在 calc() 函数中使用正确的单位。例如,角度使用 deg,百分比使用 %

总结

相对颜色语法是CSS颜色处理的一次重大升级。它让我们可以更灵活、更动态地控制颜色,减少了对预处理器和JavaScript的依赖。虽然目前还处于发展阶段,但是相信在不久的将来,它一定会成为CSS开发者的必备技能。

彩蛋

最后,给大家分享一个好玩的小技巧。你可以用相对颜色语法来创建一个简单的颜色反转效果:

.invert {
  background-color: #ff0000; /* 红色 */
  color: color(white red(calc(255 - red())) green(calc(255 - green())) blue(calc(255 - blue()))); /* 反转后的颜色 */
}

这段代码会将背景色反转,得到青色。是不是很有趣?

好了,今天的分享就到这里。希望大家有所收获!如果有什么问题,欢迎在评论区留言。咱们下期再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注