引言

  ‏‎‪‏‪‭‫‬‪‫‫‮‫‪‪‏众所周之,CSS的维护一直是件不易的事情,特别是在构建大型Web站点或Web应用程序时,如果是多人协作的话难度更大。另外,由于CSS语言是一种声明式语言,而且不像其他语言有变量、条件和逻辑等特性,一直生存在程序语言鄙视链的最底层。也因为这个原因,社区中有了各种CSS处理器语言,比如Sass、LESS和Stylus等。这些处理器语言引入了一些类似其他程序语言的特性,比如变量、运算符和逻辑运算等。

  虽然CSS处理器给编写和维护CSS带来了一些便利,但还是需要额外的编译。不过处理器中的变量着实为我们带来很大的优势,也正因为如此,社区开始将CSS处理器中的变量引入到原生CSS中,经过多年的推进和演变才有了今天的CSS自定义属性。

一、CSS 自定义属性简介

  所谓自定义属性,就是CSS作者(CSSer)自己定义的属性,这些属性统称为自定义属性,允许作者自由的选择名称,自由的为名称属性分配任意值。这些属性能够提供给var()函数使用,被var()函数引用的自定义属性又常被称为变量。
  这样一来,CSSer声明的这些自由属性就有了两个名称:自定义属性 和 变量:
  自定义属性:使用--代表任意声明的名称)声明的特殊格式作为名称,该名称被称为自定义属性,同时可以给自定义属性赋予任何值。比如--color: #fff。
  变量:CSS的var()函数引用的自定义属性被称为变量。var()会返回自定义属性所对应的值,同时可以被运用于相应的CSS属性。对应的即是CSS规则中的属性值。
  用一张图来描述他们之间的关系:

二、CSS自定义属性的作用

  当我们构建大型Web网站或Web应用时,使用的CSS数量是非常庞大的,并且在很多场合有着大量的重复使用。就拿网站的配色方案来举例,一些颜色在CSS文件中会出现很多次,并被重复使用。当你修改配色方案时,不论是调整某个颜色或完全修改整个配色,都会是一个复杂的问题。如果单纯的依赖全局的查找替换是远远不够,这样的操作难免会出错。
  如果使用了CSS的框架,这种情况会变得尤其糟糕,此时如果要修改颜色,则需要对框架本身进行修改。虽然这些框架都有可能引入了Sass这样的CSS处理器帮助我们减少了出错的机会,提高了可维护性的能力,但这种通过添加额外步骤的方式(需要做编译处理),可能会增加系统的复杂性。
CSS自定义属性(CSS变量)的出现,为我们带来了一些CSS处理器的便利,并且不需要额外的编译。在CSS中使用CSS自定义属性的好处和在编写语言中使用变量的好处没有特别的不同之处。W3C规范上有过这样的一段描述:
  使用CSS自定义属性使得大文件更易于阅读,因为看起来很随意的值有了一个提示信息的名字,并且编辑这些文件更加简单,更不易于出错。因为你只需要在自定义属性处修改一次,这个修改就会应用到使用该自定义属性的任何地方。
  简单地说,CSS自定义属性除了提供了更灵活的设置、引用和修改的便利性之外,还具有较强的语义化(这需要你对语义化有足够强的意识,比如primary这样的名称总是要比red这样的名称来得有意义)。这些语义化信息让你的CSS文件变得易读和理解。
  为此,可读性和可维护性是CSS自定义属性最大的优势。并且,它让我们可以在CSS中使用原生的变量,而不再需要借助于类似Sass这样的CSS处理器。

三、CSS自定义属性语法

3.1 自定义属性的声明

  CSS自定义属性和常规CSS属性的用法是一样的。把它们当作动态属性会比变量更好。这意味着它们只能在声明块中使用。也就是说,自定义属性和选择器是强绑定的。可以是任何有效的选择器。
  如果已声明的CSS自定义属性未被任何属性调用的话,将不会产生任何的效果。只会是一段字符串停留在你的样式文件中。
  声明自定义属性的时候,自定义属性名前面要加两根连词线(--)。

body {  --foo: #7F583F;  --bar: #F7EFD2;}

  CSS自定义属性的命名规则比较松散,可以是任何有效的字符,比如中文、大写字母、驼峰命名、中距线、emoji和HTML实体等等:

自定义属性名大小写敏感,--color 和 --COLOR 是两个不同的自定义属性。

3.2 var() 函数

  var()函数用于读取变量,将变量当作var()函数的第一个参数传进去。

a {  color: var(--foo);  text-decoration-color: var(--bar);}

  var()函数还可以使用第二个参数,表示自定义属性的默认值。如果该自定义属性不存在,就会使用这个默认值。

color: var(--foo, #7F583F);

var() 的第二个参数不处理内部的逗号或空格,都视作参数的一部分。

var(--font-stack, "Roboto", "Helvetica");var(--pad, 10px 15px 20px);

var()函数还可以用在自定义属性的声明。

:root {  --primary-color: red;  --logo-text: var(--primary-color);}
var()函数可以代替元素中任何属性中的值的任何部分。不过var()函数不能作为属性名、选择器或者其他除了属性值之外的值。
.foo {  --side: margin-top;  /* 无效 */  var(--side): 20px;}

四、CSS 自定义属性的使用

4.1 CSS自定义属性命名

  CSS自定义属性的命名规则比较松散,可以是任何有效的字符,比如中文、大写字母、驼峰命名、中距线、emoji和HTML实体等等:

4.2 CSS自定义属性和CSS属性工作原理完全相同

  CSS自定义属性可以在任何元素、选择器,甚至是伪元素上声明的普通属性。他的使用和CSS属性的使用是相同的,原理也是相同的:

:root {     --font-size: 1em; } p {     font-size: var(--font-size); } section::after {     font-size:1.5em; }

4.3 CSS自定义属性和CSS属性一样具有继承和级联特性

  举个例子,每个Web应用都会有自己的下色系,就拿Bootstrap这个CSS框架的色系来说吧,它的主色系是--primary: #007bff,该色会用于多个地方,比如:

:root{     --primary: #007bff; } // Button组件 .btn-primary {     background-color: var(--primary);     border-color: var(--primary); } // Badge组件 .badge-primary {     background-color: var(--primary);     border-color: var(--primary); } // Dropdowns组件 .dropdown-primary {     background-color: var(--primary);     border-color: var(--primary); } // Pagination组件 .page-link {     color: var(--primary); } // Progress组件 .progress-bar {     background-color: var(--primary);}

  这样,当我们想换换色系的时候,只需要调整:root中的--primary的值即可。
  自定义属性的级联写法如下:

:root {     --color: #333; } .card {     color: var(--card);     &:nth-child(2) {         --color: #2196F3;     }     &:nth-child(3) {         --color: #f321ab;     } }

对于自定义属性的继承,举个例子如下:

<!-- HTML --> <p>我是什么颜色?</p> <div>我又是什么颜色?</div> <div id="alert">     我是什么颜色?     <p>我又是什么颜色?</p> </div> // CSS :root {     --color: #333; } div {     --color: #2196F3; } #alert {     --color: #f321ab; }

4.4 CSS自定义属性可以在行内style属性中使用

  CSS自定义属性和CSS属性一样,可以在元素的style属性中使用CSS自定义属性。

<!-- HTML --> <button style="--color: blue">Click Me</button> // CSS button {     border: 1px solid var(--color); } button:hover {     background-color: var(--color); }

五、浏览器的支持性

  CSS的自定义属性是CSS的一个重要的新功能,到目前为止所有的现代浏览器中都对CSS自定义属性做了良好的支持,这将使得原生 CSS 从此变得更加强大。

参考文章:
图解CSS:CSS自定义属性
CSS变量教程 - 阮一峰