CSS自定义属性也被称为CSS变量。这是非常令人兴奋的,因为我们在CSS中终于拥有真正的变量。什么意思,真正的变量?我的意思是可以动态更新和修改变量。虽然CSS处理器有了变量(Sass和PostCSS),这些变量通过编译变成了CSS,但没有动态能力更新其变量。这不是真正的CSS变量,只是用来存储和更新可用的值。
你可以通过--
设置CSS变量和通过var(--varName)
访问已声明的变量。这是一个非常基本的方法,可以像下面声明一个变量和让文本的颜色为red
:
div {
--color: red;
color: var(--color);
}
CSS变量是非常令人兴奋的
让我们一起来聊聊,为什么动态的CSS为变量会比以前做得更好。
动态JavaScript+CSS
使用CSS变量,我们现在可以更容易地通过JavaScript更新它的值。这意味着,我们不需要使用内联属性样式或者更新类名。我们可以简单通过替换CSS变量的值。
可以像下面那样给:root
传递值:
document.documentElement.style.setProperty('--varName', 'propValue')
这样直接在你的CSS文件中写入或更新:root
的值。所以,如果我想更新一个加载条的进度比例时,可以这样做:
function calculateLoadProgress() {
let loadProgress = 0;
// codes to update loadProgress here
return loadProgress;
}
// Set width of progress bar
document.documentElement.style.setProperty('--progressBarWidth', calculateLoadProgress());
这是众多示例中的一个。@David Khourshid使用React做了一些非常有趣的探索和通过JS库确定值,然后传回给CSS变量。他在最近的CSS Conf EU上做了相关的分享与讨论。
动态属性片段
CSS变量有一个很酷的功能需要注意,那就是如何在属性值中指定CSS变量。虽然以前我们单独使用border
来声明边框,现在我们可以使用变量更新属性的任何部分,包括缩写的border
或者是不知道参数的阴影和渐变属性。
比如这个示例:
.button-gradient {
background: linear-gradient(var(--gradientAngle), var(--gradientStart),var(--gradientStop));
--gradientAngle: 60deg;
--gradientStart: lightpink;
--gradientStop: lightyellow;
}
.button-gradient:hover {
--gradientAngle: 0deg;
}
我们更新的是--gradientAngle
而不是整个background
属性。我们也可以使用JavaScript更新任何元素的这些值。是不是很不错。
干净的组件
CSS变量可以让我们写出更干净的组件,也可以更好的修改组件。下面一个是按钮的组件,让我们看看代码:
传统上,使用像BEM这样的来命名约定,我们将通过处理器,让修饰符(modifier)重写基类:
// Variables
$primaryColor: lightgreen;
$buttonBgColor: $primaryColor;
// Base Class
.button {
background: $buttonBgColor;
// other properties
}
// Modifier Class
.button--blue {
background: lightblue;
}
在上面的示例中,我们通过提高选择器权重,让后面的属性覆盖前面的属性,但文件体积增大了,我们的代码库也将会被弄乱。但使用CSS变量,我们不需要通过样式覆盖前面的样式,只需要更新CSS的变量。下面是使用CSS变量写的代码:
// Variables
:root {
--primaryColor: lightgreen;
--buttonBgColor: var(--primaryColor);
}
// Base Class
.button {
background: var(--buttonBgColor);
}
// Modifier Class
.button--blue {
--buttonBgColor: lightblue;
}
使用局部变量我们能做得更好
和上面类似,最新的文档、教程和案例中CSS变量都是在CSS文件中通过:root
来声明变量和变量值。
用来设置全局变量这是一个很好的方式,但不是必需的。CSS变量不仅仅可以在:root
中声明,它也可以在CSS文件中的任何时候声明,比如在一个CSS的代码块中指定变量。这类似于JavaScript中使用关键词let
来声明变量,可以在{}
中声明变量,这种方式声明的变量被称为局部变量。所以我们可以利用这种特性,更好的处理我们的组件样式。
Leveraging CSS Variable scope improves the size, specificity, & semantics of our stylesheets. More...
例如,--buttonBgColor
放在:root
中作为一个全局变量,并不是我们所需要的东西(比如前面的示例)。较好的做法是在<button>
内重命名变量--bgColor
。这使它与它的父组件更紧密的耦合在一起,也更具有语义化。
CSS变量的使用规则:除非你需要一个全局变量,否则尽量使用局部CSS变量。然后在你自己需要地方使用。这样可以减少CSS变量在:root
中的大量堆积,也使代码更加清晰。
在代码块
{}
中声明的变量,其作用域就像是JavaScript中使用let
关键词声明的变量一样,都是属于局部变量。
组织和示例
在Sass中,我们可以使用&
扩展和重写一个嵌套,也可以更清晰,更可视化的面向对象。在这里我们可以看一个完整的示例:
- 默认样式(指定属性)
- 默认值(基本变量)
- 差异化(更新变量)
代码如下:
.button {
// 1. Default Styles
background: var(--bgColor);
padding: var(--size);
line-height: var(--size);
border: var(--outline);
font-size: var(--size);
// 2. Default Values
--bgColor: lightgreen;
--outline: none;
--size: 1rem;
// 3. Variances
&--blue {
--bgColor: lightblue;
}
&--pink {
--bgColor: hotpink;
}
&--large {
--size: 1.5rem;
}
&--outlined {
--outline: 2px solid currentColor;
}
}
注:我们仍然可以使用:root
声明全局变量,比如基本的颜色和字号重置样式,但局部变量减少权生,也从而减小大小和增加语义化。
默认值
另外一个有趣的就是可以在:root
中声明一个默认值,以防他们不存在。var()
有另外一个能力,可以接受两个参数,并且可以嵌套在变量中。在下面的示例中,如果没有声明--bgColor
,那就Card就会调用--colorPrimary
的值,也就是red
。所以我们可以删除步骤2的中.button
的基本样式和在修饰符中更新--bgColor
(如果我们想让默认按钮的颜色是主色)。
// 0. Set global variables here
:root {
--colorPrimary: red;
}
.button {
// 1. Default Styles
// If --bgColor is not defined, the background will be the fallback: red
background: var(--bgColor, var(--colorPrimary));
// 2. Default Values
// Since --bgColor is defined, the button remains lightgreen
// If the line below was missing, the button would be red
--bgColor: lightgreen;
// ...
}
主题换肤
如果我们有更复杂的组件,我们仍然可以使用这种技术,并且可以结合像Sass一样的CSS处理器使它更简洁。可以像下面这样的来写按钮,让Card的按钮比主题的基本按钮更大。
.button--large {
.card & {
--size: 1.7rem;
}
}
我们可以在任何地方更新--size
的值,让Card的按钮变得略大一点。使用&
技术编译出来的代码:
.card .button--large {
--size: 1.7rem;
}
所以我们的结构可以这样写:
<div class="card">
<button class="button button--pink button--large">
Large Pink Button
</button>
</div>
让具有红色边框的.card
运用上面的代码。我们可以看到有一个更大的紫色按钮:
准备好了?
CSS变量得到了当今浏览器广泛的支持,尽管IE不支持,但Edge也得到了一定的支持。
如果你现在就要开始使用,有两个选择。
@supports
在CSS中,我们可以使用@support
来检测浏览器是否支持。如果你想玩一些更现代的CSS属性,比如CSS Grid,那么@support
是一个很好的工具。你可以像下面那样使用来检测你的浏览器是否支持CSS变量:
@supports(--color: red) {
// code here implementing variables
}
使用@support
是一个很好的选择,但它也像CSS的变量一样还存在很多争议。所以说,提供一份备用的值,可能是一个更好的解决方案。
传递一份备用值
你可以复用CSS的宽松风格,给同一个属性发送多个值。首先像平时一样写属性值,然后第二写声明的变量:
div {
--color: red;
color: red;
color: var(--color);
}
这是多余的,但它允许你在一个现有的代码库中慢慢缓解使用一些CSS变量,一旦得到浏览器的支持,使其更容易重构。
CSS变量是超级强大的,并且局部变量使它们成为一个更加强大的工具,能让你模块更干净,更好的模块化设计系统。
扩展阅读
- Demo I Made Sending Using JS to Rewrite CSS Variables Inline
- Types of CSS Variables, CSS Tricks
- Using CSS Variables, MDN
- Why You Should Care About CSS Variables, Rob Dodson
- Theming With Custom Properties, Harry Roberts
- Time To Start Using CSS Custom Properties, Serg Hospodarets
- It’s Time To Start Using CSS Custom Properties
- CSS Custom Properties and Theming
- CSS custom properties (native variables) In-Depth
- CSS @apply rule (native CSS mixins)
- CSS Variables Are a Bad Idea
- The power of CSS variables
- Conditions for CSS Variables
- CSS Variables — No, really!
- How to Use CSS Variables for Animation
- Autoprefixing, with CSS variables!
- Individualizing CSS Properties with CSS Variables
- Making Custom Properties (CSS Variables) More Dynamic
本文根据@Una的《Locally Scoped CSS Variables: What, How, and Why》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://una.im/local-css-vars。
如需转载,烦请注明出处:https://www.w3cplus.com/css/local-css-vars.html