CSS 自定义属性,很多人又把它称之为 CSS 变量,是由用户自己定义的用来指定 CSS 属性值的变量。来看一个简单的例子:

1
2
3
4
5
6
:root {
--color-red: red;
}
.red {
color: var(--color-red);
}

上面这个例子中,首先在 :root(文档根元素即 html )处定义了一个全局变量,变量名称叫 color-red。然后在 red 元素里使用了这个变量,那么该元素颜色将变成红色。

变量申明与使用

参照上面的例子,你可能很容易看出 CSS 变量的声明方式:使用 --* 声明一个变量。这里的 * 表示变量名称。变量名称的组成可以是数组[0-9]、字母[a-zA-Z]、下划线_或者连字符-组合而成,更厉害的是变量名称甚至可以是中文,另外变量名称是区分大小写字母的。CSS 变量的取值可以是单值 10px、复合属性的值 1px solid #ddd,计算值 calc( 10px * 2 ),甚至属性值里又可以引用变量 var(--size) 10px

1
2
3
4
5
6
7
8
/* 变量申明,注意观察变量名称和属性值的定义 */
:root {
--10: 10px;
--marginTop: 20px;
--color-red: red;
--特殊的内间距: 10px 0 20px;
--padv: calc(var(--10) * 2) 0;
}

变量的使用方式:使用 var(--*) 使用变量,其实比较完整的使用规则是这样的:var( <–变量名称> [, <默认值> ]? ),比如下面这样:

1
2
3
div {
padding: var(--size, 10px 0);
}

这个表示,当 --size 这个变量未定义的时候,则使用默认值 10px 0,注意这个地方不能加分号。

还有一种情况是,假如定义了一个变量,但是变量值是不合法的,这个时候就会取属性的缺省值。比如:

1
2
3
4
5
body {
--color: 20px;
background-color: #369;
background-color: var(--color, #cd0000);
}

看上面的例子,此时的 body 的背景颜色到底是什么呢?A. transparentB. 20pxC. #369D. #cd0000
答案是 A,因为这个时候变量 color 的值用在这里是不合法的,所以会取 background-color 的缺省值即默认替代值transparent,也不会用 #cd0000

再来看个例子:

1
2
3
4
body {
--size: 20;
font-size: var(--size) px;
}

乍看上面这个例子,body 的字号是 20px,其实不然。var(--size)px 会解析完成后会变成 20 px,这个值是不合法的,所以会取缺省值 16px。注意到这个问题,稳妥的做法还是在变量申明的时候带上单位。或者使用 calc() 来计算:

1
2
3
4
body {
--size: 20;
font-size: calc(var(--size) * 1px);
}

CSS 变量作用域

居然是变量,那么自然的有作用域这一说法,在 CSS 变量中,也是存在作用域的。全局作用域,直接将变量定义在根元素上:

1
2
3
:root {
--color-red: red;
}

局部作用域是会直接在某个元素上定义变量,且这个变量只在该元素及后代元素中有效:

1
2
3
4
5
6
body {
color: var(--color-red);
}
div.red {
--color-red: red;
}

上面的例子中,body 的颜色不会被设置成红色。另外值得一提的是,变量的申明和变量的使用与定义在文件里的先后位置无关。

当存在多个变量的时候,变量的覆盖规则由 CSS 选择器的权重决定,但并无 !important 这种用法,因为没有必要,!important设计初衷是干掉 JSstyle 设置,但对于变量的定义则没有这样的需求。

1
2
3
4
5
6
:root {
--color: red;
}
div {
--color: blue;
}

此时有一个 div 元素,那么其颜色值会是蓝色的。

来实践一下

先来看一个例子:

1
2
3
4
5
6
7
8
div {
border: 1px solid #d2d2d2;
}
div {
border-width: 1px;
border-style: solid;
border-color: #d2d2d2;
}

上面这两种设置的边框效果都是一样的,区别是一个用的是复合属性,一个是独立属性。在 CSS 里存在许许多多的属性值是由多个值组成的属性,比如 borderanimation 等。如果要单独地修改这些多个属性值里的某一个,复合属性就可以直接修改它的独立属性,但是对于那种不是复合属性却有多个属性值的 CSS 属性就头大了,比如 box-shadow,特别是在 OOCSS 的世界里,这种情况就更尴尬了。但是现在有了 CSS 自定义属性,问题就可以迎刃而解了。比如,box-shadow: 1px 2px 3px 4px #ddd,这里可以把这 5 个值分别用 5 个全局变量表示:

1
2
3
4
5
6
7
:root {
--box-shadow-offset-x: 1px;
--box-shadow-offset-y: 2px;
--box-shadow-blur: 3px;
--box-shadow-spread: 4px;
--box-shadow-color: #ddd;
}

当需要单独地修改某个值的时候,直接用 js 修改全局变量的值即可:

1
document.documentElement.style.setProperty( '--box-shadow-offset-x', '3px' );

是不是很方便,是不是很好玩,戳我给你看一个例子。

custom_property

CSS 变量存在的意义

在构建大型站点时,作者通常会面对可维护性的挑战。在这些网页中, 所使用的 CSS 的数量是非常庞大的,并且在许多场合大量的信息会重复使用。例如,在网页中维护一个配色方案,意味着一些颜色在 CSS 文件中多次出现,并被重复使用。当你修改配色方案时,不论是调整某个颜色或完全修改整个配色,都会成为一个复杂的问题,不容出错,而单纯查找替换是远远不够的。

如果使用了 CSS 框架,这种情况会变得尤其糟糕,此时如果要修改颜色,则需要对框架本身进行修改。在这些场合使用 LESSSass 类似的预处理器是非常有帮助的,但是这种通过添加额外步骤的方式,可能会增加系统的复杂性。CSS 变量为我们带来一些预处理器的便利,并且不需要额外的编译。

这些变量的第二个优势就是名称本身就包含了语义的信息。CSS 文件变得易读和理解。main-text-color 比文档中的#00ff00 更容易理解,特别是同样的颜色出现在不同的文件中的时候。

兼容性

CSS 自定义属性在 IE 上完全不支持啊,在其他浏览器上支持性还是不错的。戳我

caniuse

参考文章