共计 6460 个字符,预计需要花费 17 分钟才能阅读完成。
CSS 预处理器,如 Sass 和 Less,使得 CSS 代码易于组织和维护。通过提供变量、混合、循环等特性,使得 CSS 具有动态编写的能力,从而减少重复性工作,提高开发速度。
最近,CSS 开始添加一些动态特性。CSS 变量(自定义属性)已经加入规范,并且获得了大多数浏览器的支持。但是 CSS 混合特性还在进行中。
在这篇文章中,我们将会向你展示怎么把 CSS 变量应用到开发中,从而使得样式表更加可维护和 DRY (Don’t Repeat Yourself)。
让我们现在开始!
CSS 变量是什么?
如果你使用过任何编程语言,你肯定熟悉变量这个概念。变量让你存储和更新程序运行需要的值。
例如,考虑下面的 JavaScript 片段:
let number1 = 2;
let number2 = 3;
let total = number1 + number2;
console.log(total); // 5
number1 = 4;
total = number1 + number2;
console.log(total); // 7
number1 和 number2 是两个变量,分别存储数字 2 和 3。
total 也是一个变量,存储 number1 和 number2 变量的和,在这个例子中是 5。你可以动态更新这些变量的值,并且在程序的任何地方使用更新后的值。在上面的代码片段中,我把 number1 的值更新为 4,当我使用相同的变量再次执行加法操作时,存储在 total 中的值就变成 7,而不是 5 了。
变量的好处在于你可以把值存储在一个地方,然后在你需要的地方修改它。这样你就不用在程序的不同地方为不同的值添加不同的变量:所有变量更新使用同一个存储地址,比如你的变量。
CSS 主要是一门声明式语言,缺乏动态性。你可能会说给 CSS 添加变量会与 CSS 本身相矛盾。如果前端开发仅仅关注语义,那么给 CSS 添加变量确实会与 CSS 本身矛盾。幸运的是,网络语言更像动态语言,它会随着周围环境和开发者的需求不断变化。CSS 也不例外。
总而言之,变量已经成为 CSS 中令人激动的实现,你很快也会发现,学习和使用它非常直观。
使用 CSS 变量有什么好处?
在 CSS 中使用变量的好处和在编程语言中没有特别大的不同。
下面是规范对上述问题的回答:
[使用 CSS 变量] 使大文件更易于阅读,因为看起来很随意的值有了一个提示信息的名字,并且编辑这些文件更加简单,更不易于出错。因为你只需要在自定义属性处修改一次,然后这个修改就会自动应用到使用该变量的任何地方。
W3C 规范.
换句话说:
通过与项目相关的方式命名变量,管理和维护代码会变得更加容易。例如,如果项目的主色调保存在 –primary-color 中,修改项目的主色调就会变得很容易,仅仅改变该变量的值就可以,而不用去修改遍布在代码各处、不同 CSS 属性中的颜色值。
CSS 变量和预处理器变量的不同之处?
在给网站添加样式时,你可能已经通过预处理器,如 Sass 和 Less,体验过变量的灵活性带来的好处。
预处理器可以让你设置变量,并且在函数、循环和数学操作等中使用。这是不是意味着 CSS 变量就没有什么用处了?
不完全是,主要是因为 CSS 变量和预处理器变量并不一样。
不同之处在于 CSS 变量是运行在浏览器中的动态 CSS 属性,而预处理器变量会被编译成普通的 CSS 代码。因此,浏览器并不知道预处理器变量的存在。
这就意味着你可以更改样式表、行内样式属性和 SVG 展示型属性中的 CSS 变量,或者使用 JavaScript 操作它们。这是预处理器变量做不到的。CSS 变量提供了更多可能性!
但这并不是说你需要在二者之间选择其一:你可以同时使用 CSS 变量和预处理器变量的强大功能。
CSS 变量的语法
为了简单起见,在这篇文章中我使用了 CSS 变量这个术语,但是官方文档给出的是级联变量的 CSS 自定义属性。CSS 自定义属性形式如下:
`--my-cool-background: #73a4f4;`
在自定义属性前面添加两个短横线,然后像普通的 CSS 属性一样给它赋值。在上面的代码片段中,给 –my-cool-background 自定义属性赋了一个颜色值。
级联变量部分包括使用 var() 函数应用自定义属性,形式如下:
`var(--my-cool-background)`
自定义属性的使用范围是 CSS 选择器的内部,var() 像一个真正的 CSS 属性值被使用。
:root {--my-cool-background: #73a4f4;}
/* CSS 文件的其他部分 */
#foo {background-color: var(--my-cool-background);
}
上面的代码片段把 –my-cool-background 自定义属性定义在:root 伪元素内,这使得自定义属性的值全局可用(:root 匹配 <html> 元素内的任何元素)。然后使用 var() 函数把值应用到 ID 是 foo 的容器的 background-color 属性上,然后这个容器就会得到一个淡蓝色背景。
除此之外,还可以把淡蓝色应用到多个 HTML 元素的其他颜色属性上,如 color,border-color 等。你需要做得仅仅是通过 var(–my-cool-background) 获取自定义属性的值,然后应用到相应的属性上。当然,你需要好好考虑 CSS 变量的命名规范,使你的变量名能更好地反映变量的内容。
p {color: var(--my-cool-background);
}
查看 CodePen 上 SitePoint(@SitePoint) 的 CSS 变量运行实例。
你也可以在 CSS 变量中使用另一个 CSS 变量,举例如下:
--top-color: orange;
--bottom-color: yellow;
--my-gradient: linear-gradient(var(--top-color), var(--bottom-color));
上面的代码片段创建了 –my-gradient 变量,它的值是使用 –top-color 和 –bottom-color 变量创建的一个渐变。现在,你可以在任何地方通过仅仅改变变量的值来修改渐变,而不必到处在样式表中创建渐变实例。
下面是一个在线 CodePen 演示。
查看 CodePen 上 SitePoint(@SitePoint) 的在 CSS 变量中使用另一个 CSS 变量。
最后,在使用 CSS 变量的时候,还可以添加一个或多个后备值,举例如下:
`var(--main-color, #333);`
在上面的代码片段中,#333 是一个后备值。如果没有提供后备值,当自定义属性无效或者没有赋值的时候,会使用继承值。
CSS 变量是大小写敏感的
和一般的 CSS 属性不一样,CSS 变量是大小写敏感的。
例如,var(–foo) 和 var(–Foo) 使用的是两个不同的自定义属性,分别是 –foo 和 –Foo。
CSS 变量是级联的
类似一般的 CSS 属性,CSS 变量也会继承。例如,我们定义一个值是 blue 的自定义属性:
:root {--main-color: blue;}
<html> 根元素内的所有元素如果应用 –main-color 就会继承值 blue。
如果你在另一个元素里面给自定义属性赋了一个不同的值,这个元素的所有子元素就会继承这个新值,举例如下:
:root {--main-color: blue;}
.alert {--main-color: red;}
p {color: var(--main-color);
}
<--! HTML -->
<html>
<head>
<!-- head code here -->
</head>
<body>
<div>
<p>blue paragraph.</p>
<div class="alert">
<p>red paragraph.</p>
</div>
</div>
</body>
</html>
上面的标记语言中的第一个段落会继承全局 –main-color 的值,所以字体颜色是蓝色。
具有.alert 类的 div 元素内部的段落元素的字体颜色是红色的,因为它继承了局部范围内的 –main-color 变量,这个变量的值是 red。
查看 CodePen 上 SitePoint(@SitePoint) 的 CSS 变量继承的简单例子。
现在了解了规则,让我们开始实践吧!
在 SVG 中使用 CSS 变量
CSS 变量和 SVG 可以很好的一起工作!你可以使用 CSS 变量来修改内联 SVG 中的样式和展示型属性。
比如,你想通过 SVG 图标元素的父元素来给它一个不同的颜色。你可以在父元素内设置一个局部的 CSS 变量,然后把它赋值成你想要的颜色,然后,父元素内的图标就能从父元素继承到合适的颜色。
下面是相关代码:
/* 图标的内联 SVG symbol */
<svg>
<symbol id="close-icon" viewbox="0 0 200 200">
<circle cx="96" cy="96" r="88" fill="none" stroke="var(--icon-color)" stroke-width="15" />
<text x="100" y="160" fill="var(--icon-color)" text-anchor="middle" style="font-size:250px;">x</text>
</symbol>
</svg>
/* 图标的第一个实例 */
<svg>
<use xlink:href="#close-icon" />
</svg>
上面的标记语言使用了 <symbol> 标签,使用它可以创建不可见的 SVG 图形。然后使用 <use> 标签实例化了一个上述图形的可见版本。使用这种方式通过简单地引用 <symbol> 元素的 ID(#close-icon) 就能创建大量的图标,然后再根据你的喜好对图标进行自定义。这比重复的写同一段代码要简便的多。如果你想复习这个技术,Massimo Cassandro 在他的创造你自己的 SVG 图标中提供了一个快速教程。
注意 SVG 中的圆形元素的 stroke 属性值和文本元素的 fill 属性值:它们都使用了一个 CSS 变量,–icon-color,这个变量定义在 CSS 文档的:root 选择器上,如下所示:
:root {--icon-color: black;}
图标现在的样子如下:
如果你现在把 SVG 图标放到不同的容器中,然后在每个父元素的选择器中给这个变量赋不同的颜色值,你就能在不添加任何样式规则的情况下创建不同颜色的图标。真酷!
举个例子,把上面图标的一个实例放在一个有类.success 的 div 中。
HTML:
<div class="success">
<svg>
<use xlink:href="#close-icon" />
</svg>
</div>
现在,在.success 选择器内给 –icon-color 变量赋值 green,然后看下效果。
CSS:
.success {--icon-color: green;}
现在,图标的颜色变成了绿色:
查看下面完整的演示示例:
查看 CodePen 上 SitePoint(@SitePoint) 的 SVG 图标和 CSS 变量的基本使用。
在 @keyframes 动画中使用 CSS 变量
CSS 变量可以和 CSS 动画一起使用,不论是在一般的 HTML 元素还是内联 SVG 元素上。只需要记住在想添加动画的元素的选择器上定义自定义属性,然后使用 var() 函数在 @keyframes 中引用。
比如,给 SVG 的一个有类.bubble 的 <ellipse> 元素添加动画,CSS 代码如下:
.bubble {
--direction-y: 30px;
--transparency: 0;
animation: bubbling 3s forwards infinite;
}
@keyframes bubbling {
0% {transform: translatey(var(--direction-y));
opacity: var(--transparency);
}
40% {opacity: calc(var(--transparency) + 0.2);
}
70% {opacity: calc(var(--transparency) + 0.1);
}
100% {opacity: var(--transparency);
}
}
你可能已经注意到我们可以使用 CSS 的 calc() 通过 var() 函数对变量进行计算,这使代码更加灵活。
这个例子中使用 CSS 变量的灵活之处是通过简单的改变相应选择器内部的变量值,就可以改变动画效果,而不必查找 @keyframes 指令中的每个属性。
下面是完整的 CodePen 演示:
查看 CodePen 上 SitePoint(@SitePoint) 的使用 CSS 变量和 SVG 的简单动画。
使用 JavaScript 操作 CSS 变量
一件更酷的事情是你可以直接通过 JavaScript 代码访问 CSS 变量。
假设有一个叫 –left-pos 的 CSS 变量,它的值的 100px,定义在 CSS 文档的.sidebar 类中:
.sidebar {--left-pos: 100px;}
使用类似下面的 JavaScript 代码获取 –left-pos 的值:
// 获取你想添加动画的元素
const sidebarElement = document.querySelector('.sidebar');
// 把侧边栏元素的样式存储在 cssStyles 变量中
const cssStyles = getComputedStyle(sidebarElement);
// 获取 CSS 变量 --left-pos 的值
const cssVal = String(cssStyles.getPropertyValue('--left-pos')).trim();
// 在控制台打印 CSS 变量的值
// 控制台会输出变量的值为 100px
console.log(cssVal);
使用类似下面的 JavaScript 代码给 CSS 变量赋值:
`sidebarElement.style.setProperty('--left-pos', '200px');`
上面的代码片段把侧边栏元素的 –left-pos 变量设置成 200px。
相对于改变大量的类或者重写全部的 CSS 规则,使用 CSS 变量给网站添加交互更直接,也更易于维护。
查看下面的 CodePen 演示,你可以通过侧边栏来改变混合模式属性和背景颜色,而这仅仅需要 CSS 变量和 JavaScript:
查看 CodePen 上 SitePoint(@SitePoint) 的混合模式,CSS 变量和 JavaScript。
浏览器对 CSS 变量的支持情况
除了 IE11(不支持)和 Microsoft Edge(buggy 支持),在本文编写的时候,所有主流浏览器都完全支持 CSS 变量。
适配有问题的浏览器的方式之一是使用 @supports 进行条件查询:
section {color: gray;}
@supports(--css: variables) {
section {
--my-color: blue;
color: var(--my-color, 'blue');
}
}
因为 IE/Edge 支持 @supports,所以上面的代码会生效。如果在 var() 函数中添加一个后备值,你的代码将会更加健壮,在支持的更加不好的浏览器中也能优雅降级。
所以,在 Chrome 和其他支持 CSS 变量的浏览器中,<section> 元素内部的文本是蓝色的:
IE11 不支持 CSS 变量,文本会被渲染成灰色:
查看在线演示:
查看 CodePen 上 SitePoint(@SitePoint) 的 @supports 和 CSS 变量。
这种方式的缺点是如果你在项目中使用了大量的 CSS 变量,但是该项目主要通过不支持 CSS 变量的浏览器打开,那么代码不仅会变得有点儿复杂,维护也将会是噩梦。
在这种情况下,你可以选择使用支持 cssnext 的 PostCSS,然后你就可以编写尖端的 CSS 代码了,兼容不支持的浏览器交给 PostCSS 去做就可以了,这有点儿像 JavaScript 的编译器。如果你想了解 PostCSS,SitePoint Premium 为其所有成员提供了有关此主题的精彩视频课程。