【译】CSS变量的正确使用方法
原文:
Using CSS variables correctly原作者:
Mike Riethmuller翻译:百度外卖FE - 安秦
译者注
前端的发展真的是好快,这两年JS的发展几乎掩盖了其他前端技术的光芒,然而不知不觉中原生CSS也变得这么强大。这篇文章不是语法介绍也不是教程,看完之后却才真正地体会CSS变量的好处。
先简单说明一下CSS变量目前的规范语法:
- 用两个“-”开头表示声明变量,如:--white-color: #FFF
- 用var(...)引用变量值,如:background: var(--white-color)
译文
CSS变量(又称自定义属性)现以得到所有主流浏览器的支持,人们已经开始再生产环境中使用它们。这很好,不过它们跟各种预处理器中的变量是不一样的,而我见过很多例子,写代码的人并没有意识到CSS变量真正的优势。
它们终究会改变我们CSS的写法以及思维方法。我认为我有必要做进快速的demo来演示一些CSS变量的正确或者错误的使用案例,挺尸也演示它们与预处理器的区别是如何改变我们CSS代码结构的。
有什么区别?
首先,它们区别在哪里?最主要的区别是,CSS变量可以改变。这听起来一点也不让人惊讶,变量肯定能变。你可能没仔细想过,像Sass这样的预处理器,里面的变量是静态的。的确,你可以在编译过程中的不同时间点改变一个变量的值,但是当它被翻译成CSS以后他们就是静态的了。
预处理器里的变量是个非常的好的工具,能够帮助我们写出很DRY(don't repeat yourself:不冗余)并且易于维护的CSS代码。CSS变量不太一样,它可以响应页面上下文变化。
变量作用域可以是静态或动态的,CSS变量作用域是动态的。
具体来说,动态作用域意味着可以存在继承与层叠。这很棒,因为这样你的可以在媒体查询里或者符合某种选择器的元素里改变变量的值。同样的变量可以在页面的不同位置有不同的值。你甚至可以用JavaScript读取或更改CSS变量。
如果你没有仔细想过多少CSS变量的用法,看完这篇文章你就见识到了。不过我会先演示一下CSS变量的错误用法。
CSS变量实现缩放模块化
我将以模块化(modular scale)为例。缩放模块化是一种尺寸缩放方法,可用于给标题元素设置合适的尺寸。我很喜欢这么做,而且还喜欢给不同的屏幕尺寸设置不同的缩放比例。
我想要给小屏幕设置1.2倍缩放,大屏幕1.33倍。我不喜欢算数所以我从 http:// modularscale.com 获取以下的数值并用作我的标题元素尺寸:
不要这么做…
这是个使用CSS变量的完美场景。如果使用Sass的思考方式,也是我如今见过很多人用CSS变量的方式,会是如此:
:root {
/* 1.2倍缩放 */
--ms-small-1: 1em;
--ms-small-2: 1.2em;
--ms-small-3: 1.44rem;
--ms-small-4: 1.728rem;
--ms-small-5: 2.074rem;
--ms-small-6: 2.488rem;
/* 1.33倍缩放 */
--ms-large-1: 1rem;
--ms-large-2: 1.333rem;
--ms-large-3: 1.777rem;
--ms-large-4: 2.369rem;
--ms-large-5: 3.157rem;
--ms-large-6: 4.209rem;
这看起来很合理,我们为每一种尺寸定义了相应的变量。然后我想就会看到这:
/* 小屏幕有小的缩放比例: */
h1 {
font-size: var(--ms-small-6);
h2 {
font-size: var(--ms-small-5);
h3 {
font-size: var(--ms-small-4);
h4 {
font-size: var(--ms-small-3);
h5 {
font-size: var(--ms-small-2);
h6 {
font-size: var(--ms-small-1);
/* 大屏幕有大的缩放比例 */
@media screen and (min-width: 800px) {
h1 {
font-size: var(--ms-large-6);
h2 {
font-size: var(--ms-large-5);
h3 {
font-size: var(--ms-large-4);
h4 {
font-size: var(--ms-large-3);
h5 {
font-size: var(--ms-large-2);
h6 {
font-size: var(--ms-large-1);
正常运行!更好的是,如果我希望改变任何一个值我只需要在一处更改。在CSS的其他地方使用变量还会带来更多的好处。
就像Sass一样,这很DRY,比普通的CSS要好很多。然而我们可以做得更好。
要像这样做……
上面的例子看起来逻辑很严谨,但是它并没有真正利用到CSS变量的真正原理。我们再来一次,这次记得CSS变量是根据DOM产生作用域的,所以可以有集成与层叠。
:root {
/* scale for 1.2 */
--font-size-1: 1em;
--font-size-2: 1.2em;
--font-size-3: 1.44rem;
--font-size-4: 1.728rem;
--font-size-5: 2.074rem;
--font-size-6: 2.488rem;
@media screen and (min-width: 800px) {
:root {
/* scale for 1.33 */
--font-size-1: 1rem;
--font-size-2: 1.333rem;
--font-size-3: 1.777rem;
--font-size-4: 2.369rem;
--font-size-5: 3.157rem;
--font-size-6: 4.209rem;
注意我这次只有一组变量,而不是每种缩放比例都有一组。我是根据屏幕尺寸改变变量的值。这种写法导致:
- 我必须改变变量的命名方式(不再有small或large)
- CSS的其他地方不再需要有媒体查询
我现在可以在我的属性定义当中直接使用变量,并且知道它们会根据需要而改变。所有响应式逻辑都在变量里。剩下的CSS就如下即可:
h1 {
font-size: var(--font-size-6);
font-size: var(--font-size-5);
font-size: var(--font-size-4);
font-size: var(--font-size-3);
font-size: var(--font-size-2);
font-size: var(--font-size-1);
以上的例子展示了一种更好的方式来使用CSS变量。
CSS变量代码结构的组织技巧
变量是可以根据CSS结构而变化的,尤其在于响应式设计相关的方面。
逻辑与设计分离:
最大的优势是,我们现在可以完全分离逻辑与设计。更明确的说法是,我们可以把变量声明与属性声明分开。
.this-is-a-variable-declaration {
--my-var: red;
.this-is-a-property-declaration {
background: var(--my-var)
}
在用预处理器的时候,把变量与其余的定义声明分开是很好的实践方式,用CSS变量时也应如此。
改变值而不是变量:
在大多数情况中,我认为 在媒体查询或选择器中用另一个变量覆盖原有的变量是很臭的写法 。与其改变引用的变量,更好的做法是只定义一个变量,设置一个厨师值然后在媒体查询或选择器中改变它的值。
如果它会变那就是个变量:
我相信在多数情况下,响应式设计逻辑应该用变量实现。 这里还有过很激烈的讨论,不论是在媒体查询或是元素作用域中,如果有任何属性值是需要变化的,它就应该使用变量。如果它会变,那么它从字面上讲就是个变量,那么这个逻辑就应该与设计分离。
减少媒体查询:
讲道理,所有与变量相关的逻辑都应该放到文档的最开头。这样更好维护,因为你可以在一个地方改变它们,这样也更好阅读,因为你不用看整个样式代码就可以知道有哪些东西会发生变化。
用媒体查询达不到这样的效果,因为它会需要把对同一个元素的样式定义分成碎片并分布到代码的不同位置。这样的做法操作麻烦还不好维护,所以只好把媒体查询与被他们影响的选择器放到一起。
用变量就可以将逻辑与设计的实现联系起来。 这就是说,通常媒体查询除了修改变量值就不再有别的需求了 ,然后它们应该存在于文档的开头语变量声明放在一起。以上,“逻辑折叠”。
简化选择器:
将逻辑与设计里可以简化你的主要属性声明以至于你可以把很多选择器直接组合在一起。
这个例子,我有一个侧边栏和一个主元素,它们拥有不同的字体大小。侧边栏有深色背景而主元素有浅色背景。
/* 变量默认值 */
:root {
--font-size: 1.2em;
--background-color: #fff;
--text-color: #222;
/* 侧边栏的变量值 */
aside {
--font-size: 1em;
--background-color: #222;
--text-color: #FAFAFA;
/* 相同的属性声明 */
main,
aside {
font-size: var(--font-size);
color: var(--text-color);
background-color: var(--background-color);
虽然拥有完全不一样的样式,这两个元素却拥有一样的属性定义。
减少通用变量:
提一个警告:不要使用过于通用的变量。你也许会觉得搞个全局选择器,然后使用变量实现所有的逻辑很有趣:
/* 别这样 */
display: var(--display);
width: var(--width);
height: var(--height);
border: var(--border);
background: var(--background);
虽然很好玩,但是我们应当谨慎地复用变量以及合并选择器。CSS变量会响应层叠定义,按照上面的例子,当给.container如下设置边框时:
.container {
--border: solid 2px tomato;
所有在这个容器里的元素都会继承相同的边框。很快你就需要给所有的元素重设变量,不用 * 全局选择器就不会栽入这个坑。
利用预处理器使用静态变量:
CSS变量可以完全替代预处理器?并不能,使用预处理器依然有意义。所有的静态变量保持使用Sass(或其他什么你正在用的预处理器)是很好的想法。
// 静态变量:
$breakpoint-small: 600px;
$theme-color: rebeccapurple;
// 动态变量