前段时间试译了 Keith J.Grant 的 CSS 好书《CSS in Depth》,其中的第二章 《Working with relative units》,书中对 relative units 的讲解和举例可以说相当全面,看完之后发现自己并不太懂 CSS 相对单位,也希望分享给大家,所以有了这个译文系列。(若有勘误或翻译建议,欢迎 Github PR ^_^)
《别说你懂 CSS 相对单位》系列译文:
CSS 自定义属性 [本文] 本文对应的章节目录:
2.6 自定义属性(也叫“CSS 变量”)
总结
在今年年初,译者也写过一篇 《CSS Variables 学习笔记》,里面有更多的 CSS Variables 的语法解释和实例 demo,感兴趣的朋友可以看看:)
在 2015 年,一个大家期待已久的名为“用作层叠式变量的自定义属性”(Custom Properties for Cascading Variables)的 CSS 规范终于发布为“候选推荐标准”(Candidate Recommendation)。这套规范引入了 CSS 中“变量”的概念,支持一种新的基于上下文的动态样式定义方式。你可以声明一个变量,再给它赋值,然后就可以在样式表的任何地方引用它。你可以通过这样的方式,减少样式表中的重复代码,以及后续你会看到的一些有用的应用场景。
在写这本书的时候,自定义属性已经被大多数主流浏览器支持了,除了 IE。查看最新的浏览器支持情况,可以查看 Can I Use 的 http://caniuse.com/#feat=css-variables。
笔记
如果你刚好在用支持自定义变量的 CSS 预处理器,如 Sass(syntactically awesome stylesheets)或 Less,你可能会下意识拒绝 CSS 变量。千万别这么做。因为原生的 CSS 变量比任何一个预处理器能实现的功能都要强大和灵活。为了强调它们之间(原生 CSS 变量和预处理器自定义变量)的差异,我会把它叫作“自定义属性”,而不用“CSS 变量”。
声明一个自定义属性,跟声明其他属性类似。代码片段 2.23 是自定义属性声明的例子。新建一个页面和样式表吧,然后添加以下的 CSS 代码。
[ 代码片段 2.23 声明一个自定义属性 ]
:root {
--main-font: Helvetica, Arial, sans-serif;
}
代码片段中,定义了一个名叫 --main-font
的变量,然后把它的值设定为普通的字体 sans-serif。为了和其他属性区分开,命名的前缀必须是两道横杠(--
),然后写上你想要的名字。
变量一定要声明在一个声明区块内。在这里,我使用了 :root
选择器,那么这个变量就可以在整个页面的样式里使用 —— 后面我会简单解释这个问题。
变量的声明,就它本身而言,不会做任何事情,直到我们在代码里引用它。我们在一个段落中使用它吧,做成像图 2.13 那样的效果。
[ 图 2.13 对一个简单段落使用用变量声明的字体 sans-serif ]
我们可以用一个叫作 var()
的函数去引用自定义属性的值。现在,你可以利用这个函数去引用我们刚才声明的变量--main-font
。把下面展示的代码片段添加到你的样式表中吧,把变量用起来。
[ 代码片段 2.24 使用一个自定义属性 ]
:root {
--main-font: Helvetica, Arial, sans-serif;
}
p { 1
font-family: var(--main-font); 1
}
brand-color
的自定义属性。你可以在样式表中多次使用这个变量,但假如你需要(全局)修改它的值,只需要在一行代码中编辑它的值就可以了。[ 代码片段 2.25 对 color 使用自定义属性 ]
:root {
--main-font: Helvetica, Arial, sans-serif;
--brand-color: #369; 1
}
p {
font-family: var(--main-font);
color: var(--brand-color);
}
brand-color
变量 var()
函数支持第二个参数,代表一个默认值。假如一个变量被声明的时候,第一个参数没有被声明,那么第二个参数值就会被引用。[ 代码片段 2.26 提供回退默认值 ]
:root {
--main-font: Helvetica, Arial, sans-serif;
--brand-color: #369;
}
p {
font-family: var(--main-font, sans-serif); 1
color: var(--secondary-color, blue); 2
}
--main-font
被声明,值为Helvetica, Arial,sans-serif
,于是这个值就会被用到了。第二个声明里,--secondary-color
是一个没有声明过的变量,所以默认值 blue 被用到了。笔记
如果var()
被定义为一个无效值,这个属性会被定义为它的初始值。举个例子,如果在padding: var(--brand-color)
中,变量是一个色号,那对于 padding 来说这就是一个无效值。在这个情况下,padding 的值会被定义为 0。
从这些例子可以看到,自定义属性只是更方便了一点,也可以帮助你减少很多的重复代码。但让自定义属性更有意思的是,自定义属性的声明是可以层叠和继承的。你可以在多个选择器中声明同一个变量,这些变量在页面的不同部分可以有着不一样的值。
你可以声明一个变量是黑色的,举个例子,然后在一个特定的容器里把它重新定义为白色的。于是,在这个容器以外的所有依赖这个变量的颜色是黑色,而在容器内的就是白色。通过这样的方式,我们来实现一个像图 2.14 这样的效果。
[ 图 2.14 自定义属性基于不同域下的值,生成两个颜色不一样的面板 ]
这个面板类似你之前看到的那个(图 2.7),HTML 在代码片段 2.27。这个面板有两个实例,一个在 body 下,另一个在一个深色的区块。来,更新下你的代码。
[ 代码片段 2.27 页面上不同上下文的两个面板 ]
<body>
<div class="panel"> 1
<h2>Single-origin</h2>
<div class="body">
We have built partnerships with small farms
around the world to hand-select beans at the
peak of season. We then careful roast in
small batches to maximize their potential.
</div>
</div>
<aside class="dark"> 2
<div class="panel"> 2
<h2>Single-origin</h2>
<div class="body">
We have built partnerships with small farms
around the world to hand-select beans at the
peak of season. We then careful roast in
small batches to maximize their potential.
</div>
</div>
</aside>
</body>
[ 代码片段 2.28 利用变量定义面板的颜色 ]
:root {
--main-bg: #fff; 1
--main-color: #000; 1
}
.panel {
font-size: 1rem;
padding: 1em;
border: 1px solid #999;
border-radius: 0.5em;
background-color: var(--main-bg); 2
color: var(--main-color); 2
}
.panel > h2 {
margin-top: 0;
font-size: 0.8em;
font-weight: bold;
text-transform: uppercase;
}
:root
选择器里。很明显,这样的话我们就可以在根元素(整个页面)下的任何元素中引用这个变量了。当根元素下的子元素使用这些变量时,它们就能拿到这些变量对应的值。你有两个面板,不过它们仍然看起来是一样的。现在,再一次定义这些变量,但这次是在一个不同的选择器中。下一个代码片段是深色容器的,它有深灰色的背景色,以及小小的 padding 和 margin。同时,它也重写了两个变量。添加到你的样式表吧。
[ 代码片段 2.29 设置深色容器的样式 ]
.dark {
margin-top: 2em; 1
padding: 1em;
background-color: #999; 2
--main-bg: #333; 3
--main-color: #fff; 3
}
在这个例子里,你两次定义了自定义属性,第一次在根元素作用域上(--main-color
是黑色的),第二次在深色容器作用域(--main-color
是白色的)。自定义属性表现得像作用域变量,因为值会被后代元素继承。在深色容器中,--main-color
是白色的,而在页面的其他位置,它是黑色的。
在浏览器中,自定义属性还可以被 JavaScript 访问和动态地修改。毕竟这不是一本讲 JavaScript 的书,我会告诉你足够多的基本概念,然后你再把这些融入到自己的 JavaScript 项目中。
[ 代码片段 2.30 在 JavaScript 里访问一个自定义变量 ]
<script type="text/javascript">
var rootElement = document.documentElement;
var styles = getComputedStyle(rootElement); 1
var mainColor = styles.getPropertyValue('--main-bg'); 2
console.log(String(mainColor).trim()); 3
</script>
--main-bg
动态地定义一个新的值。如果你把它定义为浅蓝色,它就是展示成这样(图 2.15)。[ 图 2.15 JavaScript 可以通过改变变量—main-bg 的值改变面板的背景色 ]
下面的代码片段,会在根元素下给 --main-bg
定义一个新的值,在<script>
标签的最下面,加上这些的代码。
[ 代码片段 2.31 在 JavaScript 定义一个自定义变量的值 ]
var rootElement = document.documentElement;
rootElement.style.setProperty('--main-bg', '#cdf'); 1
--main-bg
属性的元素都会发生改变,对应的值会变成新的。在你的页面上,这会把第一个面板的背景色变成浅蓝色。第二个面板保持不变,因为它继承的还是在深色容器里定义的值。利用这项技术,你可以在浏览器里用 JavaScript 给你的站点换主题。或者你可以高亮页面上的某些部分,又或者随手就可以做一些改变。只需要少量几行 JavaScript 代码,你做的改变就可以影响到页面上大量的元素。
自定义属性是一个全新的 CSS 领域,开发者才刚刚开始探索。因为目前浏览器的支持比较有限,所以还没有到使用它的“黄金时间”。我相信,一段时间之后,你会看到很多关于自定义属性的最佳实践和新颖的玩法。这是你需要留意的。尝试使用自定义属性,看看你可以做出些什么吧。
需要关注的一点,如果你使用 var()
声明,低版本浏览器不能识别就会忽略它。如果可以的话,给那些浏览器提供一个回退(fallback)方案。
[ 代码片段(没有编号) ]
color: black;
color: var(--main-color);
自定义属性原生的动态特性,并不是总是可以使用的,可以关注它的浏览器支持情况 http://caniuse.com。
《别说你懂 CSS 相对单位》系列译文:
CSS 自定义属性 [本文] 章节:
2.1 相对单位值的魔力
2.2 em 和 rem
2.3 停止使用像素思维去思考
2.4 视口相关单位(viewport-relative units)
2.5 不带单位的数字(unitless number)和行高(line-height)
2.6 自定义属性(也叫“CSS 变量”)
总结
原著版权信息:
作者:Keith J.Grant
书籍:CSS in Depth
章节:Working with relative units
笔者 @Yuying Wu,前端爱好者 / 鼓励师 / 铲屎官。目前就职于某大型电商的 B2B 前端团队。
感谢你读到这里,对上文若有任何疑问或建议,欢迎留言。
如果你和我一样喜欢前端,喜欢捣腾独立博客或者前沿技术,或者有什么职业疑问,欢迎关注我以及各种交流哈。
独立博客:wuyuying.com
知乎 ID:@Yuying Wu
Github:Yuying Wu