说到 CSS 变量,我们想到更多的是 CSS 预处理器。CSS 在以前就是简单的样式表,维护起来特别麻烦,通常需要使用类来增强可维护性,所以出现了 SASS 和 LESS 等预处理器帮助我们更好的维护项目的样式代码。

CSS 原生变量的出现虽然并没有改变预处理器的市场,因为预处理器不仅仅提供变量这个功能,但是原生变量的出现仍然使得我们在某些方面的操作更加便捷且易维护,其还可以与预处理器一起使用。

本文引自: 海致前端公众号《CSS3原生变量初探》
另外,推荐阮一峰的《CSS 变量教程》

1. 原生变量的优势

对比 CSS 预处理器变量,原生 CSS 变量有很多优点:

  • 原生意味着不需要经过任何转译步骤,因为它是浏览器原生支持的。所以你不需要像使用 Sass 和 Less 那样先进行配置,无需任何设置就能直接书写这样的代码,也比预处理器变量编译出的文件简洁直观
  • CSS 变量是运行在浏览器中的动态 CSS 属性,而不像预处理器变量会被编译成普通的 CSS 代码,这使得在内联样式和 SVG 的标签中可以直接更新 CSS 变量,或直接使用 JavaScript 操作它们,来改变一批元素
  • 就像常规的 CSS 属性一样,作为DOM的一部分动态生效。可以在@media等媒体查询块中使用,可以在运行时进行修改,也可以向DOM添加新类
  • 支持作用域,这就意味着我们可以在不同的层级定义相同名称的变量。如果某元素没有CSS变量,则会继承父元素的
  • 和预处理器中的变量并不冲突,可享有它们各自带来的巨大好处,提供更多可能性

2. 变量声明

CSS 原生使用 — 两个中横线来声明变量,Sass 使用的是 $ ,Less 使用的是 @ ,选用双中横线是为了避免冲突。

  1. .block {
  2. --color: blue;
  3. }

与预处理器不同的是,使用变量需要先确定其作用域,也就是必须在某个选择器内进行定义。如果想让他全局生效,只需在 :root 伪类中定义它即可,它会匹配文档的根元素(通常是标签)。

  1. :root {
  2. --main-color: red;
  3. }

CSS变量的变量名对大小写敏感,变量的值可以是颜色、字符串、多个值的组合等等。

  1. .block{
  2. --main-color: red;
  3. --main-bg: rgb(255, 255, 255);
  4. --logo-border-color: rebeccapurple;
  5. --header-height: 68px;
  6. --content-padding: 10px 20px;
  7. --base-line-height: 1.428571429;
  8. --transition-duration: .35s;
  9. --external-link: "external link";
  10. --margin-top: calc(2vh + 20px);
  11. --黑色: #000;
  12. border: 1px solid var(--黑色);
  13. }

变量名称局限在数字、字母、下划线、中横线,但是可以使用中文、日文或韩文。

3. 变量使用

var 的使用

和预处理器不同,原生CSS变量不能直接引用变量名来使用。你需要通过 var() 函数来引用变量。

  1. :root {
  2. --main-color: red
  3. }
  4. #foo {
  5. background-color: var(--main-color);
  6. }

你可以将该变量应用在多个html元素的属性上,所以需要好好考虑CSS变量的命名规范,使你的变量名能更好地反映变量的内容。
你也可以在CSS变量中使用另一个CSS变量。

  1. :root {
  2. --top-color: red;
  3. --bottom-color: blue;
  4. --gradient-box: linear-gradient(var(--top-color), var(--bottom-color));
  5. }

现在,你可以在任何地方通过仅仅改变变量的值来修改渐变,而不必到处在样式表中创建渐变实例。

在原生 CSS 中,你只能将变量设置为属性值。在需要进行计算时,可以使用 CSS 的 calc() 函数。

  1. .margin {
  2. --space: calc(20px * 2);
  3. font-size: var(--space); /*等于 40px*/
  4. }

var 的完整结构

var()的完整结构如下:

  1. var() = var( <custom-property-name> [, <declaration-value> ]?)

其中 custom-property-name 为想要使用的变量,如前面的’—space’。declaration-value 为缺省值,避免自定义的属性无效时产生错误。如果写成逻辑式大致是:

  1. if (custom-property-name) {
  2. --space
  3. } else {
  4. declaration-value
  5. }

JS 更新变量

CSS变量也可以通过JavaScript访问,直接更新变量的值。

  1. /* CSS部分 */
  2. :root {
  3. --main-color: red
  4. }
  5. /* JS部分 */
  6. var root = document.querySelector(':root');
  7. var rootStyles = getComputedStyle(root);
  8. var mainColor = rootStyles.getPropertyValue('--main-color');
  9. console.log(mainColor); --> 'red'

要更新CSS变量,只需在已声明变量的元素上调用 setProperty 方法,并将变量名称作为第一个参数,将新值作为第二个参数传入。

  1. root.style.setProperty('--main-color', 'blue')

如果 CSS 变量中有一个无效的值,或拼写错误,那么 CSS 属性将默认采用默认值。

  • background 默认值是 transparent
  • width 默认值是 auto
  • position 默认值是 static
  • opacity 默认值是 1
  • display 默认值是 inline

另外,即使 CSS 变量中的值无效,依然也可以被 JavaScript 读取。这意味着可以把样式设置写入 CSS 变量,让 JavaScript 读取。提供了一种 JavaScript 与 CSS 通信途径。

  1. --foo: if(x > 5) this.width = 10;

上面代码中,—foo的值在 CSS 里面是无效语句,但是可以被 JavaScript 读取。

4. 应用场景

媒体查询

因为 CSS 变量是动态的,且可以在 @media 命令里声明变量。所以能方便的在不同屏幕宽度下设置不同的变量。

  1. :root {
  2. --main-gap: 1rem;
  3. }
  4. @media (min-width: 30rem) {
  5. :root {
  6. --main-gap: 2rem;
  7. }
  8. }
  9. @media (min-width: 48rem) {
  10. :root {
  11. --main-gap: 3rem
  12. }
  13. }
  14. .Cont {
  15. padding: var(--main-gap);
  16. }
  17. .Grid {
  18. --gapNeg: calc(-1 * var(--main-gap));
  19. display: flex;
  20. margin-left: var(--gapNeg);
  21. margin-top: var(--gapNeg);
  22. }
  23. .Grid-cell {
  24. flex: 1;
  25. margin-left: var(--main-gap);
  26. margin-top: var(--main-gap);
  27. }

可以看出在使用了 CSS 变量时,能大大减少完成相同操作所需的代码量。而且配置越多,越能减少代码冗余。

创建有动态效果的组件

构建可复用组件的话,使用 CSS 变量是一个非常好的选择。

  1. .btn {
  2. padding: 2rem 4rem;
  3. border: 2px solid var(--color, black);
  4. background: transparent;
  5. font-size: 0.6em;
  6. border-radius: 2px;
  7. }
  8. .btn:hover {
  9. cursor: pointer;
  10. background: var(--color, black);
  11. color: white;
  12. }
  13. .btn.red {
  14. --color:red
  15. }
  16. .btn.bule {
  17. --color:blue
  18. }
  19. .btn.green {
  20. --color:green
  21. }

可以看出,组件的可变项越多,CSS变量减少的重复代码就越多。

使用CSS变量实现主题定制

因为可以使用 JavaScript 直接来控制 CSS 变量的值,可以直接改变单一 CSS 变量就影响整个页面中所以引用了这个变量的地方,代码变得简介干净。

  1. body {
  2. --theme-color: red;
  3. --theme-background: black;
  4. }
  5. a {
  6. color: var(--theme-color);
  7. background: var(--theme-background);
  8. }
  9. /*js更新部分*/
  10. document.body.style.setProperty('--theme-color', 'blue');
  11. document.body.style.setProperty('--theme-background', 'white');

5. 兼容性

虽然 CSS 变量是 CSS3 新出属性,但是它的浏览器兼容性却表现良好。下图为 CSS 变量在各个浏览器下支持情况。由此可以看出,CSS 变量在所有现代浏览器都支持良好,唯有 IE 浏览器基本不支持。但因为国内 IE 的占有量依然不少,在所以项目对 IE 兼容性有要求的可参考后面的解决方案。

CSS 原生变量 - 图1

可以在这里查看完整兼容列表:Can I use

在做老式兼容浏览器兼容时可使用如下方法:

  1. p {
  2. color: red;
  3. color: var(--primary);
  4. }

在 Sass 等预处理器中可使用下面方法达到自动兼容:

  1. $vars: (
  2. primary: red,
  3. );
  4. body {
  5. --primary: map-get($vars, primary);
  6. }
  7. @mixin var($property, $varName) {
  8. #{$property}: map-get($vars, $varName);
  9. #{$property}: var(--#{$varName}, map-get($vars, $varName));
  10. }
  11. /*使用*/
  12. a {
  13. @include var(color, primary);
  14. }

输出的 CSS 代码是这样的:

  1. a {
  2. color: red;
  3. color: var(--primary, red);
  4. }

使 IE 兼容 CSS 变量的插件:css-variables-IE-plugin