【译】深入了解CSS自定义属性
原文链接:CSS custom properties (native variables) In-Depth
原文发布时间:March 29, 2016
原作者:Serg Gospodarets翻译者:IwYvI
这将是我翻译的第一篇文章,主要是关于CSS自定义属性等内容。(这篇文章看上去翻译起来比较简单,(不知道我翻译完了还会不会这么说
词汇翻译:
- CSS property:CSS属性
- CSS custom property:CSS自定义属性
- CSS variable:CSS变量
翻译这篇文章借助了有道、bing、Google的翻译。如果出现不准确的地方希望能向我提出来
在这篇文章中,我本想从解释CSS变量的用途开始说起,但是事实上许多流行的CSS预、后处理器已经实现了这个功能
几个用途的例子:
- 颜色变量
- 组件的常量属性(布局,位置属性等等)
- 避免代码冗余
当然,你仍然可以手动在你的代码库里查找或者替换你所想修改的部分,但是这就像在JS中没有定义变量直接使用硬编码的值一样痛苦。动态的和限定作用域的CSS变量将对你的实验和应用程序提供更多功能:你可以自由地阅读,设置和更新它们!同时你可以避免在代码库中造成代码冗余,你可以看看最近的这篇文章Webkit guys did in their Web Inspector。
同时你终于有了一个可以轻松地从CSS向JS传递数据的接口(例如媒体断点的值(media breakpoint values))
下面是CSS属性具有的几个特点:
- 它们是动态的,能在运行时被修改
- 它们可以轻松地用JS读写
- 它们是可继承的,层叠的且有作用域
下面,让我们深入了解什么是CSS属性和怎么使用它们
名称
最开始,这项特性被叫做CSS变量,但是后来被扩展和重构为CSS自定义属性
然而,根据它所使用的语法,更准确的名称应该是CSS属性。这篇相关文章CSS @apply rule让我们觉得有点”mixins”的感觉。
所以目前的名称是CSS Custom Properties for Cascading Variables(CSS层叠变量的自定义属性)。
变量是指一个标识符指代一个任意常规的值。
var()
符号:var(--example-variable)
将返回--example-variable
的值。
自定义属性是指用
--*
这样的形式表示的属性,其中*
代表变量名称。用这种形式给变量赋值:--example-variable: 20px;
,这句话表示将变量--example-variable
赋值为20px
。
第一个CSS变量
或许这会让你惊讶,因为你有可能已经了解并且使用过一个CSS变量(看起来是第一个)currentColor
,这个变量虽然并不是众所周知的但是仍然可以使用并且可以在所有浏览器中工作。
它同样有作用域并且可以被重新定义:
1 | :root { color: red; } |
如果你加上这一句
1 | div { |
边框的颜色将变回黑色
CSS变量的语法
赋值
你可以使用--variable-name: variable-value;
这个语法来定义一个变量(变量名是大小写敏感的)。而变量的值可以是颜色,字符串等等:
1 | :root{ |
语法似乎看起来很吃藕,但是这是因为种种原因。例如$var
这样的变量语法就会被其他的CSS预处理器处理。
用法
你可以在CSS像这样用变量:some-css-value: var(--variable-name [, declaration-value]);
1 | p { |
在上面这个例子中如果--p-margin
没有被定义,0 0 10px
将被使用。这样的特性会让编写的代码更灵活,例如你可以使用一些来自框架的变量(通常多数变量已经被定义了),但是当你要移除它们的时候这个特性将会节约做其他【高端】的事的时间。
作用域
正如module’s documentation title里提到的,自定义属性也遵守CSS层叠规则。
用:root scope
来创建一个全局变量:
1 | :root{ |
如果你想创建一个只在某个元素或组件里存在的变量,就在那个元素里面[重]定义这个变量(demo):
1 | <div class="block"> |
1 | .block { |
媒体查询同样提供了作用域(demo):
1 | @media screen and (min-width: 1025px) { |
下一个关于作用域的例子是伪类(例如::hover
)(demo):
1 | body { |
当定义了全局的自定义变量,为了避免命名冲突,可以看看这篇文章a common convention naming your variables(或者更简单地用BEM naming convention这个方法来命名),例如:
1 | :root { |
用其他变量来赋值
同样我们可以用其他变量来给变量赋值--variable-name: var(--another-variable-name);
(demo):
1 | .block { |
这里有一个小问题,就是不能简单地用已定义的变量来计算出新的变量值。但是我们可以用calc()
来代替(demo):
1 | .block { |
注意,那些很庞大的表达式可能会影响到应用的性能
使用calc()计算
上面已经提到你不能像这样使用变量:
1 | padding: var(--spacer)px |
但是可以使用calc()
来进行计算。让我们做一个vertical rhythm(这个怎么翻译嘤嘤嘤)的例子:
1 | margin: 0 0 calc(var(--base-line-height, 0) * 1rem); |
重置或继承变量的值
CSS自定义属性默认是继承的。这种情况下为了减小对块或组件的副作用,你可以重置自定义属性:
1 | .with-reset { |
用JavaScript控制自定义属性
你可以使用JS轻松地读写自定义属性。(CSS样式声明接口(getPropertyValue
,setProperty
)):
1 | // READ |
下面是一个DEMO,使用``–screen-category`这个自定义变量代表当前显示类型,它可以在UI界面中被修改。(demo)
这个demo中展示了JS中对自定义变量进行debug的方法:
1 | // GET |
现在我们能把任意值赋给CSS变量,也有了接口在JS读写这些变量,这样我们就可以摆脱以前使用的从CSS/SASS中向JS传递数据这样的方法。(例如:list of media queries breakpoints)。
把变量放在content
中,将会在页面上输出它的值,这样可以用来做debug:
1 | body:after { |
浏览器支持
自定义属性已经可以在Chrome,Firefox和桌面版Safari 9.1里正常使用了:
Stats from caniuse.com
这个特性在Microsoft Edge浏览器里的支持还在考虑中
它们目前还有一些限制和bug:
- 用
calc()
计算CSS变量可能会在某些浏览器上出错 - 人们还在讨论为当前作用域中的自定义属性添加一些基础规则,例如
--: initial;
- 不能使用常规CSS属性名称:
var(--side): 10px;
- 用
calc()
计算时需要这样写:calc(var(--base-line-height, 0) * 1rem)
- 不能当做媒体查询的值
@media screen and (min-width: var(--desktop-breakpoint))
- 图片的url也不可以使用变量
url(var(--image-url))
这里有个DEMO可以用来测试浏览器对CSS自定义属性的支持。
1 | @supports ( (--a: 0)) { |
1 | if (window.CSS && window.CSS.supports && window.CSS.supports('--a', 0)) { |
对于以前的浏览器(没有 CSS.sgpports() API),你可以看看Wes Bos’ test。
Fallbacks/polyfills
(这个标题似乎并没有什么中文词语能直接翻译,大意就是让老浏览器支持新功能)
接下来这两段翻译的实在是有些勉强
There are couple examples of PostCSS plugins, but
no plugin can achieve true complete parity according to the specification because of the DOM cascade unknowns
+ they are not dynamic.It might be solved when we see the bright future and CSS Houdini group dream of implementing an easy native way for CSS “polyfills” to all major browsers will come true. And even in that case variables syntax, most of all, cannot be shimmed.
现在已经有许多关于PostCSS插件的例子,但是这些插件都不能根据规范真正实现对等处理,因为DOM层叠结构对它们是未知的,而且这些插件所做的处理也是非动态的。
这个问题最终将会被解决,CSS Houdini组织提出的an easy native way for CSS “polyfills” to all major browsers,应该会实现,我们也可以看得出CSS广阔的前景。最重要的是在这种情况下,变量语法不再会被忽视。
然而到目前为止有这样一个清单:
- 转换W3C CSS自定义属性的PostCSS插件- 一个只处理
:root
里声明的变量的插件 - 将CSS自定义属性(CSS变量)的语法转换为静态的语句,这有一个在线的DEMO。它尝试处理了媒体查询、伪类元素和元素嵌套这些部分的作用域问题。
- Myth- 一个预处理器
- CSS next,为CSS添加新的语法
和CSS预处理器(SCSS)一起使用
相同的变量名
现在开始和预处理器一起使用CSS自定义属性,我们可以使用一种混合语法来检查浏览器的支持
1 | @supports ( (--a: 0)) { |
在这种情况下CSS和Sass的变量都存在,但是只有在浏览器不支持自定义属性时Sass变量才会被使用。
或者你可以把这种逻辑藏在Sass的mixin中:
1 | @mixin setVar($varName, $value){ |
全局变量
在Sass和CSS中变量的作用域是不同的,但是它们都可以这样做:
1 | /* SCSS */ |
给没有赋值的变量赋值
一个常见的情况是,我们希望使用一个变量给另一个变量赋值时,如果用于赋值的变量本身没有被赋值,则需要一个默认值来赋给新的变量。
1 | /* SCSS */ |
然而你并不能在CSS里这样做:
1 | /* CSS */ |
但是你可以创建一个新的变量
1 | /* CSS */ |
或者直接使用
1 | /* CSS */ |
有趣的用法
自定义属性提出了这些有趣的想法:
- 现在可以使用原生的方法来处理CSS与JS的通信,而不是以前用的hack方法
- 另一个例子是国际化地使用自定义属性,不同语言中的text和colors可以使用
external link
来解决 - Jake Archibald 提出了一个想法:根据加载到页面的块和样式,使用CSS变量控制元素的可见性(文章链接)
- 主题切换:现在不必再为特定class添加CSS样式或者增加新的CSS样式文件来改变网站主题,你可以使用自定义属性来解决这个问题。Michael Scharnagl在这篇文章中描述了如何处理主题切换。
- 我还想到了一些用法,例如用于特定域名(对 domain1.site.com和domain1.site.com提供不同的外观)(原文如此)。这样我们就可以很轻松上传并应用重定义了自定义属性的CSS文件(取决于域名)
最后一个想法很贴近使用自定义属性的主题切换,所以可以把他们放在一起用(demo)
- 当然,自定义属性在补全CSS属性这里看起来也很好用
Demo
受到Wes Bos demos of interacting with CSS custom properties的启发,我决定更进一步在CSS中使用calc();
对颜色的R,G,B值进行计算。
这是灰度过滤器的代码:
1 | .grayscale { |
有趣的事实:
- Chrome似乎并不喜欢
calc()
中的CSS变量的非整数乘除法 - Firefox完全不计算
rgba()
里面需要使用calc()
计算的自定义变量 - Demo在Safari里能达到预期效果O(∩_∩)O~~(原本是个emoji表情)
结论
现在你应该已经知道了CSS自定义变量的含义和如下几点:
- 它的语法支持CSS和JS的交互
- 它是动态的、可继承的、层叠的和有作用域的
- 浏览器的支持和它的fallbacks
- 它可以和Sass变量一起使用
- 总之,通过一些有趣的用法和示例,自定义属性确实为开发者和web平台加入了新的能力
我希望读完这篇文章后你能用上自定义属性
扩展阅读
近期CSS原生mixins语法发布 - 【译】CSS @apply规则(mixins)
原文链接:CSS custom properties (native variables) In-Depth
原文发布时间:March 29, 2016
原作者:Serg Gospodarets
翻译者:IwYvI
翻译时间:2016/4/22
第一次翻译英语文章,如有不准确的地方希望向我提出来