CSS中的变量使用说明
很多流行的CSS预处理/后处理程序已经实现了样式变量概念,实际上大部分浏览器也实现了原生css变量功能,本篇日志讲解css变量使用说明。文章大部份内容分来自:© w3cplus.com
以下几点是未来CSS属性的简短说明:
- 动态性,可以在运行时更改
- 可以方便的从JS中读/写
- 可继承,可组合,同时具有作用域
CSS中的变量给了我们诸多优点:方便、代码重用、更可靠的代码库和提升防错能力。CSS变量用起来很方便,但是浏览器对其支持情况不太好
概要
示例
1 | :root { |
基础
当使用CSS变量时,你应该了解的三个主要组成:
- 自定义属性
- var()函数
- :root伪类
自定义属性
你已经知道了一些CSS的标准属性,如color,margin,width和font-size。
下面的示例使用了CSS的color属性:1
2
3body {
color: #000000; /* The "color" CSS property */
}
自定义属性仅意味着我们(创建CSS文件的人)去定义属性的名字。
为了自定义一个属性名,我们需要用–作为前缀。
如果我们要创建一个值为黑色的、名为text-color的自定义属性,可以这样做:1
2
3:root {
--text-color: #000000; /* A custom property named "text-color" */
}
var()函数
为了应用自定义属性,需要利用var()函数,否则浏览器不知道他们代表什么。
如果想要在p、h1和h2中的样式中使用–text-color,可以这样使用var()函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18:root {
--text-color: #000000;
}
p {
color: var( --text-color );
font-size: 16px;
}
h1 {
color: var( --text-color );
font-size: 42px;
}
h2 {
color: var( --text-color );
font-size: 36px;
}
其等价于:1
2
3
4
5
6
7
8
9
10
11
12
13
14p {
color: #000000;
font-size: 16px;
}
h1 {
color: #000000;
font-size: 42px;
}
h2 {
color: #000000;
font-size: 36px;
}
自定义属性是大小写敏感的(和普通的CSS规则不一样)
:root伪类
我们需要一个地方来放置自定义属性。虽然可以在任何样式规则中指定自定义属性,但到处定义属性并不是一个好主意,这对CSS的可维护性和可阅读性是一个挑战。
:root 伪类代表了HTML文档的根元素,这是一个放置自定义属性的好位置,因为我们可以通过其他更具特异性的选择器来覆盖自定义属性值。
第一个CSS变量
令人感到惊讶的是,你们可能已经了解或使用过一个CSS变量(可以看做是第一个)—— currentColor,它并不出名,但仍然可用,并且在所有浏览器工作。
它也有一个作用域,并且能够被重新定义:1
2
3
4
5
6
7:root {
color: red;
}
div {
/* border-color is red */
border: 1px solid currentColor;
}
如果你加上下面代码:1
2
3div {
color: black;
}
边框将变成黑色。
CSS变量语法
定义
用这样的方式来声明一个变量:–variable-name: variable-value;(变量名是大小写敏感的)。
变量的值可以是颜色、字符串、多个值的组合等:1
2
3
4
5
6
7
8
9
10
11:root{
--main-color: #4d4e53;
--main-bg: rgb(255, 255, 255);
--logo-border-color: rebeccapurple;
--header-height: 68px;
--content-padding: 10px 20px;
--base-line-height: 1.428571429;
--transition-duration: .35s;
--external-link: "external link";
--margin-top: calc(2vh + 20px);
}
可能语法看起来有些丑陋,却是有原因的。比如不能使用像$var这样的变量语法,因为已经被其他的CSS预处理程序使用了。
用法
以这样的方式来使用一个变量: some-css-value: var(–variable-name [, declaration-value]);1
2
3p {
margin: var(--p-margin, 0 0 10px);
}
在上面的例子中,如果–p-margin没有被指定,则使用值:0 0 10px。这样的设计在使用时非常灵活–你可以使用一些来自某个框架的变量(通常大部分变量定义在这里),同时当你想要移除它的时候,可以保持现有功能正常工作。
作用域
正如文档中提到的:自定义属性遵循CSS级联规则
使用:root 作用域来定义全局变量:1
2
3:root{
--global-var: 'global';
}
如果想让某个变量只在部分元素/组件下可见,只需要在特定的元素下定义该变量:1
2
3
4
5
6
7
8
9
10
11
12
13<div class="block">
My block is
<div class="block__highlight">awesome</div>
</div>
.block {
--block-font-size: 1rem;
font-size: var(--block-font-size);
}
.block__highlight {
--block-highlight-font-size: 1.5rem;
font-size: var(--block-highlight-font-size);
}
媒体查询也可以提供作用域:1
2
3
4
5@media screen and (min-width: 1025px) {
:root {
--screen-category: 'desktop';
}
}
下面一个例子来展示伪类下的作用域(例如,:hover):1
2
3
4
5
6
7
8
9body {
--bg: #f00;
background-color: var(--bg);
transition: background-color 1s;
}
body:hover {
--bg: #ff0;
}
由于自定义属性是全局的,为了避免冲突,最好按照统一的约定来命名变量(或者简单的遵循BEM命名法来形成”命名空间“),例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15:root {
/* main (page-wide) variables */
--main-color: #555;
--main-bg: rgb(200, 200, 200);
/* accordion variables */
--accordion-bg: #f00;
--accordion-font-size: 1.5rem;
--accordion__item-bg: #ded;
}
body {
color: var(--main-color);
background-color: var(--main-bg);
/*...*/
}
变量组合
变量可以和其他变量组合使用,–variable-name: var(–another-variable-name);:1
2
3
4
5
6
7
8
9
10
11
12.block {
--block-text: 'This is my block';
--block-highlight-text: var(--block-text)' with highlight';
}
.block:before {
content: var(--block-text);
}
.block__highlight:before {
content: var(--block-highlight-text); /*This is my block with highlight*/
}
这里有个问题 – 声明新变量的值不能直接由一个已定义的变量计算而来,但我们可以使用CSS calc()来代替:1
2
3
4
5
6
7
8
9
10
11
12.block {
--block-font-size: 1rem;
}
.block__highlight {
/* DOESN'T WORK */
--block-highlight-font-size: var(--block-font-size)*1.5;
font-size: var(--block-highlight-font-size);
/* WORKS */
font-size: calc(var(--block-font-size)*1.5);
}
对复杂的表达式要格外留心,它们很可能会影响到应用的性能。
值的计算(calc())
正如上文提到的,不能简单的这样使用变量:1
padding: var(--spacer)px
但借助calc()即可以实现上面功能以及其他的计算。看一个简单的例子:1
margin: 0 0 calc(var(--base-line-height, 0) * 1rem);
最后,可以随时重置/继承变量的值
CSS自定义属性默认是继承的,在这个例子中,通过重置自定义属性,可以消除模块/组件受到的影响:1
2
3
4
5.with-reset {
--bgcolor: initial;/* RESETS VALUE */
--color: green;/* CHANGES VALUE */
--border: inherit;/* DOESN'T CHANGE ANYTHING, AS INHERITED BY DEFAULT */
}
在JS中使用原生属性
使用CSS样式声明接口,可以在JS中方便的读/写自定义属性(getPropertyValue, setProperty):1
2
3
4
5
6
7// READ
const rootStyles = getComputedStyle(document.documentElement);
const varValue = rootStyles.getPropertyValue('--screen-category').trim();
// WRITE
document.documentElement.style.setProperty('--screen-category', value);
下面是使用自定义属性--screen-category的例子----screen-category变量描述了当前屏幕类型,同时在UI上可以被组合使用。
例子中展示了一种简单的调试自定义属性的方法。JS代码:1
2
3
4
5
6
7
8
9
10
11
12// GET
alert(
getComputedStyle(document.documentElement).getPropertyValue('--screen-category').trim();
);
// SET
document.documentElement.style.setProperty('--screen-category', 'custom');
// or reassign from an another prop
document.documentElement.style.setProperty(
'--screen-category', 'var(--default-screen-category, '%DEFAULT VALUE IF VAR IS NOT SET%')'
);
CSS变量值的组合能力以及JS提供的方便的读写接口,可以让我们告别老的从CSS/Sass中传数据给JS的hack方式(例如:媒体查询断点列表)。
如果是为了调试, 可以通过content在页面上输出变量的值:1
2
3body:after {
content: '--screen-category : 'var(--screen-category);
}
浏览器支持
CSS自定义属性已经在正式版Chrome,Firefox和桌面版Safari 9.1中获得支持:
同时也在微软Edge浏览器的考虑支持中
目前有一些限制和错误:
- 在一些浏览器中,针对CSS变量的复杂calc()运算可能不能工作。
- 在当前作用域下的所有自定义属性上应有公共规则(如,reset)的功能在讨论中。就像这样:–: initial;
- 不能使用CSS自定义属性作为CSS属性名称:var(–side): 10px;
- 进行calc()运算时,最好能提供默认值: calc(var(–base-line-height, 0) * 1rem)
- 不能作为媒体查询值使用:@media screen and (min-width: var(–desktop-breakpoint) {
- 图片地址,如url(var(–image-url)) ,不会生效
这是检测浏览器是否支持CSS自定义属性的方法。CSS:1
2
3
4
5
6
7@supports ( (--a: 0)) {
/* supported */
}
@supports ( not (--a: 0)) {
/* not supported */
}
JS:1
2
3
4
5if (window.CSS && window.CSS.supports && window.CSS.supports('--a', 0)) {
alert('CSS properties are supported');
} else {
alert('CSS properties are NOT supported');
}
对于老的浏览器(没有CSS.supports()方法),可以使用Wes Bos的测试方法。
Fallbacks / polyfills
有很多PostCSS插件的实例,但他们的实现没有完全正确而又和标准一致的,更重要的是,他们都不是动态的。
CSS Houdini团队的针对所有主流浏览器的一种简单的原生CSS “polyfills”方法即将到来,这无疑为未来带来曙光。但即便如此绝大多数的变量语法依然不能很好的支持。
但目前为止,下面这些依然是值得关注的:
- PostCSS plugin to transform W3C CSS Custom Properties – 简单的插件,只能处理:root作用域下的变量声明
- PostCSS插件,用来将CSS自定义属性(CSS变量)转换为静态的表达式 ,还有它的线上实例.
- Myth- 一个预处理器
- cssnext可以让许多CSS的新语法立即能够正常工作。
- 和CSS预处理器(SCSS)一起使用
相同变量名
有一个小建议,使用浏览器支持检测,来开始CSS自定义属性和预处理器的协同使用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19@supports ( (--a: 0)) {
/* Custom properties are supported in the browser */
:root{
--main-bg: #4d4e53;
}
body {
background-color: var(--main-bg);
}
}
@supports ( not (--a: 0)) {
/* Custom properties are NOT supported in the browser */
$main-bg: #4d4e53;
body {
background-color: $main-bg;
}
}
在这个实例中,CSS变量和Sass变量都被创建了,但只有在浏览器不支持CSS自定义属性时,Sass变量才会生效。
你也可以去除这段逻辑,利用Sass的mixin来隐藏它:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24@mixin setVar($varName, $value){
@include setVarSass($varName, $value);
@include setVarCss($varName, $value);
}
@mixin setPropFromVar($propName, $varName){
@supports ( (--a: 0)) {
// Custom properties are supported in the browser
#{$propName}: getVarCss($varName);
}
@supports ( not (--a: 0)) {
// Custom properties are NOT supported in the browser
#{$propName}: getVarSass($varName);
}
}
// SET
@include setVar('main-color', #f00);
// GET
body {
@include setPropFromVar('color', 'main-color');
}
全局变量
变量作用域的相关理念在Sass和CSS中是不同的,这里是一个通用的实现方法:1
2
3
4
5
6
7/* SCSS */
$main-color: #f00 !global;
/* CSS */
:root{
--main-color: #f00;
}
只关联没有被关联使用的变量
一个常见的情况是,当你期望可能已经定义的变量,在未被分配时才被应用到另一个值:1
2
3
4
5
6
7
8
9
10
11
12/* SCSS */
$main-color: #f00 !default;
body{
color: $main-color;
}
不幸的是,在CSS中不能简单的就使用它:
/* CSS */
body{
--main-color: var(--main-color, #f00); /* DOESN'T WORK */
}
但是你可以创建一个新变量1
2
3
4
5
6
7
8
9
10
11/* CSS */
body{
--local-main-color: var(--main-color, #f00); /* DOES WORK */
color: var(--local-main-color);
}
或者在使用的时候这样做:
/* CSS */
body{
color: var(--main-color, #f00); /* DOES WORK */
}
总结
现在你已经知道了什么是CSS自定义属性,还有:
- CSS和JS的语法级互动
- 动态性、可继承、可组合,同时拥有作用域
- 浏览器支持以及如何可靠地使用可以和Sass变量一起使用
- 自定义变量拓展了开发者和web平台的能力,随之产生了一些有趣的用法和案例
Chrome 使用CSS变量calc()不支持乘/除以非整数
Firefox不支持rgba()内的calc()自定义属性
Safari支持Demo演示 :blush:
###扩展阅读
CSS的原生mixins语法已经公布 – 更多文章:CSS @apply rule (native CSS mixins)