CSS 变量(更确切地说是 CSS 自定义属性)现已登陆 Chrome 49。它们不仅有助于减少 CSS 中的重复,还有助于实现强大的运行时效果,例如主题切换以及可能对未来的 CSS 功能进行扩展/填充。
CSS 杂乱
在设计应用时,常见做法是预留一组品牌颜色,以便重复使用这些颜色以保持应用外观的一致性。遗憾的是,在 CSS 中反复重复这些颜色值不仅是一件繁琐的工作,而且容易出错。如果在某个时候,其中一种颜色需要改变,您可以谨慎对待,然后“查找和替换”所有元素,但如果项目足够大,则很容易变得危险。
最近,许多开发者开始使用 SASS 或 LESS 等 CSS 预处理器来通过使用预处理器变量解决这个问题。虽然这些工具极大地提高了开发者的工作效率,但它们使用的变量也存在一个重大缺点,那就是它们是静态的,无法在运行时更改。添加在运行时更改变量的功能不仅可以开启动态应用主题等功能,还可以对自适应设计产生重大影响,并有可能对未来的 CSS 功能进行 polyfill。随着 Chrome 49 的发布,这些功能现在以 CSS 自定义属性的形式提供。
自定义属性简要说明
自定义属性为我们的 CSS 工具箱添加了两项新功能:
- 作者能够为具有作者所选名称的属性分配任意值。
var()
函数,允许作者在其他属性中使用这些值。
这个简单的示例
:root {
--main-color: #06c;
}
#foo h1 {
color: var(--main-color);
}
--main-color
是作者定义的自定义属性,值为 #06c。请注意,所有自定义属性都以两条短划线开头。
var()
函数会检索自定义属性值,并将其自身替换为自定义属性值,从而生成 color: #06c;
。只要您在样式表中的某个位置定义了该自定义属性,var
函数就应该可以使用该属性。
开始时,语法可能看起来有点奇怪。许多开发者会问:“为什么不直接使用 $foo
作为变量名称?”这种方法专门用于尽可能灵活,并且将来可能支持 $foo
宏。关于背景故事,您可以阅读规范作者 Tab Atkins 的这篇博文。
自定义属性语法
自定义属性的语法很简单。
--header-color: #06c;
请注意,自定义属性区分大小写,因此 --header-color
和 --Header-Color
是不同的自定义属性。虽然从表面上看,它们看起来很简单,但自定义属性允许使用的语法实际上非常宽松。例如,以下是一个有效的自定义属性:
--foo: if(x > 5) this.width = 10;
虽然该参数在用作变量时没有什么用处,因为它在任何常规属性中都无效,但可能会在运行时使用 JavaScript 读取和执行操作。这意味着,自定义属性有可能发掘目前的 CSS 预处理器无法实现的各种有趣技术。因此,如果您想要“打哈欠,我有 SASS,谁在意...”的话,不妨再看看!这些不是您习惯使用的变量。
级联
自定义属性遵循标准级联规则,因此您可以定义同一属性的不同特异性级别
:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
<p>I inherited blue from the root element!</p>
<div>I got green set directly on me!</div>
<div id="alert">
While I got red set directly on me!
<p>I’m red too, because of inheritance!</p>
</div>
这意味着,您可以利用媒体查询中的自定义属性来协助自适应设计。例如,随着屏幕尺寸的增加,增加主要版块元素周围的外边距:
:root {
--gutter: 4px;
}
section {
margin: var(--gutter);
}
@media (min-width: 600px) {
:root {
--gutter: 16px;
}
}
请务必注意,使用目前的 CSS 预处理器无法定义媒体查询中的变量时,无法实现上述代码段。这种能力可以释放巨大的潜力!
此外,自定义属性也可以从其他自定义属性派生值。这对于主题设置非常有用:
:root {
--primary-color: red;
--logo-text: var(--primary-color);
}
var() 函数
如需检索和使用自定义属性的值,您需要使用 var()
函数。var()
函数的语法如下所示:
var(<custom-property-name> [, <declaration-value> ]? )
其中,<custom-property-name>
是作者定义的自定义属性的名称(例如 --foo
),<declaration-value>
是在引用的自定义属性无效时使用的后备值。后备值可以是一个以英文逗号分隔的列表,它们将合并为一个值。例如,var(--font-stack,
"Roboto", "Helvetica");
定义了 "Roboto", "Helvetica"
的回退。请注意,与用于外边距和内边距的简写值一样,这些值不采用逗号分隔,因此适当的内边距后备值如下所示。
p {
padding: var(--pad, 10px 15px 20px);
}
借助这些后备值,组件作者可以为其元素编写防御性样式:
/* In the component’s style: */
.component .header {
color: var(--header-color, blue);
}
.component .text {
color: var(--text-color, black);
}
/* In the larger application’s style: */
.component {
--text-color: #080;
/* header-color isn’t set,
and so remains blue,
the fallback value */
}
此方法对于为使用 Shadow DOM 的 Web 组件特别有用,因为自定义属性可能会遍历阴影边界。Web 组件的作者可以使用回退值创建初始设计,并以自定义属性的形式公开主题“钩子”。
<!-- In the web component's definition: -->
<x-foo>
#shadow
<style>
p {
background-color: var(--text-background, blue);
}
</style>
<p>
This text has a yellow background because the document styled me! Otherwise it
would be blue.
</p>
</x-foo>
/* In the larger application's style: */
x-foo {
--text-background: yellow;
}
使用 var()
时,需要注意一些问题。变量不能是属性名称。例如:
.foo {
--side: margin-top;
var(--side): 20px;
}
但是,这并不等同于设置 margin-top: 20px;
。相反,第二个声明无效,并作为错误抛出。
同样,您也不能(简单地)构建由变量提供部分的值:
.foo {
--gap: 20;
margin-top: var(--gap)px;
}
同样,这并不等同于设置 margin-top: 20px;
。如需构建值,您需要使用 calc()
函数。
使用 calc() 构建值
如果您以前从未使用过 calc()
函数,那么您可以借助它通过执行计算来确定 CSS 值。所有现代浏览器均支持该属性,并且可以与自定义属性结合使用以构建新值。例如:
.foo {
--gap: 20;
margin-top: calc(var(--gap) * 1px); /* niiiiice */
}
使用 JavaScript 中的自定义属性
如需在运行时获取自定义属性的值,请使用所计算的 CSSStyleDeclaration 对象的 getPropertyValue()
方法。
/* CSS */
:root {
--primary-color: red;
}
p {
color: var(--primary-color);
}
<!-- HTML -->
<p>I’m a red paragraph!</p>
/* JS */
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
// value = 'red'
同样,如需在运行时设置自定义属性的值,请使用 CSSStyleDeclaration
对象的 setProperty()
方法。
/* CSS */
:root {
--primary-color: red;
}
p {
color: var(--primary-color);
}
<!-- HTML -->
<p>Now I’m a green paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'green');
您还可以通过在调用 setProperty()
中使用 var()
函数,将自定义属性的值设置为在运行时引用另一个自定义属性。
/* CSS */
:root {
--primary-color: red;
--secondary-color: blue;
}
<!-- HTML -->
<p>Sweet! I’m a blue paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'var(--secondary-color)');
自定义属性可以引用样式表中的其他自定义属性,您可以想象一下这会如何产生各种有趣的运行时效果。
浏览器支持
目前,Chrome 49、Firefox 42、Safari 9.1 和 iOS Safari 9.3 支持自定义属性。
演示
试用示例,一窥现在借助自定义属性可以利用的所有有趣技术。
深入阅读
如果您想详细了解自定义媒体资源,Google Analytics(分析)团队的 Philip Walton 撰写了一篇有关为什么他对自定义媒体资源感到兴奋的入门指南。您还可以访问 chromestatus.com,继续在其他浏览器中跟踪自定义媒体资源的进展。