WebKit 中的 CSS 变量
CSS 变量,官方名称为 CSS 自定义属性,于 2015 年秋季添加到 WebKit。WebKit 一直对在 CSS 中添加变量的想法很感兴趣,自 2008 年 Dave Hyatt 添加我们的第一个实验性实现以来。从那时起,CSS 自定义属性又进行了两次重写,这有助于塑造了当今浏览器厂商正在采纳的标准。
CSS 变量正如开发者所期望的那样,可以将一个值分配给一个自定义 CSS 符号,并在网页的样式表中重复使用。CSS 很快变得复杂,并且通常需要大量的重复代码,因此变量的好处是显而易见的。变量可以减少重复代码的数量,并简化复杂 CSS 系统的维护。
多年来,开发者一直使用预处理器作为 Web 开发工具链的一部分,在 CSS 中享受类似变量的功能。然而,与预处理器不同的是,原生的 CSS 变量为 CSS 这种主要静态的语法带来了动态变量。事实上,CSS 变量是具有 DOM 上下文的自定义 CSS 属性。这是一个重要的特性,它使得 CSS 自定义属性能够做预处理器中的变量无法做到的事情。这些是实时属性,可以根据规则的级联而变化。理解这种细微的差异可以释放 CSS 自定义属性所提供的全部潜力。
自定义属性语法
乍一看,与其他语言中的变量语法相比,这种语法可能显得有点奇怪。它被设计为使用简写前缀来融入现有的 CSS 语法。要定义一个自定义属性,创建一个 CSS 元素规则,并在其中添加一个带有双破折号前缀的自定义属性名
#foo {
--default-color: #08c;
}
通常,您会使用 :root
伪选择器来定义自定义属性的默认值,以便它们在任何地方都可用
:root {
--default-color: #08c;
}
要使用变量属性,请使用 var()
函数,传入自定义属性名,并在自定义属性可能不可用时选择性地提供一个备用值
#foo h1 {
color: var(--default-color, black);
}
由于 var()
函数产生一个值,它可以用于任何值的位置,例如在其他 CSS 函数内部,如 calc()
或 linear-gradient()
。
.gradient {
width: 66%;
height: 240px;
background: linear-gradient(120deg, rgb(100, 0, var(--less-blue, 255)), rgb(100, 255, var(--more-blue, 0)));
}
.gradient:hover {
--less-blue: 100;
--more-blue: 255;
}
在上图中,自定义属性根据正常的 :hover
行为规则一次只应用于一个渐变。
CSS 自定义属性也可以在 JavaScript 中访问
element.style.getPropertyValue('--less-blue');
值得注意的是,上面的 JavaScript 提供了 element
继承的自定义属性值,而不是在 :root
伪选择器规则中设置的值。要获取在根元素上设置的自定义属性值,请获取应用于 document.documentElement
的样式
var rootStyles = getComputedStyle(document.documentElement);
var defaultColor = rootStyles.getPropertyValue('--default-color');
由于这些是运行时属性,因此值可以动态更新
element.style.setProperty('--less-blue', '75');
document.documentElement.style.setProperty('--default-color', '#c80');
简化 Web 检查器
对于一个实际的例子,看看 WebKit 中的 Web 检查器即可,在那里使用 CSS 自定义属性使得样式简化、精简了一些复杂的 CSS 规则,并使未来的维护更加容易。这些优势在相同程度上是使用 CSS 预处理器变量无法实现的。
作为一款 Web 应用,Web 检查器的界面由 Web 技术驱动,但它的设计旨在看起来和工作起来就像一个原生操作系统应用。为了在 Web 检查器处于窗口中时保持用户体验,需要模拟原生的窗口行为。例如,当窗口状态从活动变为非活动时,窗口控件和框架元素的外观需要模拟原生非活动窗口的行为——通过降低对比度来减弱窗口的视觉影响。

为了实现这个效果,Web 检查器的 CSS 基于一个 window-inactive
类,其中包含覆盖正常外观的规则。覆盖规则改变颜色以降低对比度。在 CSS 自定义属性出现之前,没有单一规则的方法来覆盖所有需要调整的元素的所有属性的颜色。以前的方法是为元素设置一个正常的活动状态颜色规则,并在文档主体上设置 window-inactive
类时,使用一个覆盖规则来微妙地改变颜色。
.sidebar.left {
border-right: 1px solid hsl(0, 0%, 70%);
}
.sidebar.right {
border-left: 1px solid hsl(0, 0%, 70%);
}
body.window-inactive .sidebar.left {
border-right-color: hsl(0, 0%, 85%);
}
body.window-inactive .sidebar.right {
border-left-color: hsl(0, 0%, 85%);
}
相反,CSS 自定义属性允许使用一个覆盖来统治所有。默认状态有一个初始的自定义属性定义,然后直接更改新窗口状态的自定义属性值
:root {
--border-color: hsl(0, 0%, 70%);
}
body.window-inactive {
--border-color: hsl(0, 0%, 85%);
}
这使得继承承担了所有工作,从而可以放弃针对特定元素的状态覆盖,转而只需引用可变的自定义属性。
.sidebar.left {
border-right: 1px solid var(--border-color);
}
.sidebar.right {
border-left: 1px solid var(--border-color);
}
Web 检查器中声明 CSS 变量的 4 行更改用 var(--border-color)
替换了 32 个重复的颜色值,同时消除了 23 条规则!
要点总结
CSS 自定义属性为架构样式表解锁了更好的技术,可以显著减少使用的 CSS 代码,并使大型 CSS 系统的管理变得更加容易。控制主题颜色变化只是一个起点。想象一下使用级联动态变量来管理字体、间距、背景、元素位置或命名自定义过渡计时曲线的可能性。
反馈
您可以在WebKit Nightly Build 中或在 iOS 9.3 上的 Safari 9.1 以及 OS X El Capitan 10.11.4、Yosemite 和 Mavericks 中试用 CSS 自定义属性。请告诉我们您是如何使用 CSS 自定义属性的,并通过 Twitter (@webkit, @jonathandavis) 或提交 Bug 提供反馈。