CSS变量(自定义属性)实用指南及注意事项

10年服务1亿Tian开发工程师

注:在《A Practical Guide to CSS Variables (Custom Properties)》基础上增加了很多说明示例及需要注意的点。

像 Sass 和 Less 这样的预处理器有助于保持CSS代码的组织和可维护性。 通过变量,混合,循环等特性 – 为CSS编码添加动态功能 – 有助于最大限度地减少重复并加快开发速度。

近年来,一些动态特性已经开始成为 CSS 语言本身的一部分。 – 官方的术语为 “自定义属性” – 已经已经加入规范并且具有很好的浏览器支持,而 。

在本文中,您将了解如何开始将CSS变量集成到CSS开发工作流程中,让你的样式表更好维护,且。

让我们一起深入了解吧!

什么是CSS变量?

如果您使用过任何本赛季语言,那么您已经熟悉了变量的概念。变量用于存储和更新你的程序所需要的值,以便使它运行。

例如,请考虑以下JavaScript代码段:

let number1 = 2;
let number2 = 3;
let total = number1 + number2;
console.log(total); // 5
number1 = 4;
total = number1 + number2;
console.log(total); // 7

nubmer1number2 是两个变量,分别存储着数字 2 和 3 。

total 同样是变量,存储着 number1number2 之和。在这里它的值就是 5。你可以动态地修改变量里的值,并在程序中使用它们。在上面的代码中,我把 number1 的值更新为 4,然后再进行求和。使用相同的变量,这个时候 total里存储的值就不再是 5 ,而是 7 了。

变量的好处在于你可以把值存储在一个地方,然后在你需要的地方修改它。这样你就不用在程序的不同地方为不同的值添加不同的变量:所有变量更新使用同一个存储地址,比如你的变量。

CSS在很大程度上是一种声明式的语言,缺乏动态性。你也许会认为,让 CSS 拥有变量,似乎让上面的说法自相矛盾。如果Tian开发仅仅关注语义,那可以这么说。幸运的是,Web的本赛季语言很像生活中的语言,它们会随着周围环境和实践需求而不断进化与适应。CSS也不例外。

总而言之,变量已经成为 CSS 中令人激动的实现,你很快也会发现,对于这个厉害的新技术,学习和使用起来都非常直观。

使用CSS变量有什么好处?

使用CSS变量的好处,跟在其他本赛季语言中使用变量的好处没什么大的区别。

以下是规范对此的说法:

[使用CSS变量]可以更容易地阅读大文件,因为看似任意的值,现在具有信息性名称,并且使此类文件更容易编辑,且更不容易出错,因为,你只需要在自定义属性中改变一次值,所有应用了这个变量的地方都会自动跟着一起改变。

换句话说:

通过给变量起一个对你来说在项目中有意义的名字,你能更容易的管理和维护你的代码。例如,当你为项目中的主色调设置一个变量名--primary-color ,那么你后面再修改这个主色调时,只需要改动一处,而不需要在不同位置的多个CSS文件中去手动多次修改这个值。

CSS变量和预处理器中的变量有什么不同?

你可能已经在CSS预处理器中尝试过使用变量而带来的好处了,比如 Sass 和 。

预处理器让你能设置变量,以及在函数、循环、数学计算等等地方中使用它们。这是否意味着CSS变量已经无关紧要了呢?

那可未必,主要是因为,CSS变量与预处理器中的变量其实并不是同样的东西。

不同之处在于CSS变量是运行在浏览器中的动态CSS属性,而预处理器变量会被编译成普通的CSS代码。因此,浏览器并不知道预处理器变量的存在。

这意味着,你可以在样式表,内联样式和SVG的标签中直接更新CSS变量,或者使用JavaScript操作它们。这是预处理器变量做不到的。CSS变量提供了更多可能性!

但这并不是说你需要在二者之间选择其一:没有什么东西限制你,你可以同时使用CSS变量和预处理变量,并享有它们各自带来的巨大好处。

CSS变量:语法

虽然本文为了简洁,我使用了CSS变量(CSS variables)这个术语,但是官方的规范把它们称作为 级联变量的CSS自定义属性CSS自定义属性形式如下:

--my-cool-background: #73a4f4;

在自定义属性前面添加双横线前缀,然后像普通的CSS属性一样给它赋值。在上面的代码片段中,我给 --my-cool-background 自定义属性赋了一个颜色值。

级联变量(cascading variable) 的部分,由通过 var() 来使用你的自定义属性,形式如下:

var(--my-cool-background);

自定义属性的作用范围限定在 CSS 选择器中, var() 部分用作实际 CSS 属性的值:

:root {
    --my-cool-background: #73a4f4;
}

/* CSS文件的其他部分 */
#foo {
    background-color: var(--my-cool-background);
}

上面的代码片段把 --my-cool-background 自定义属性的作用域定义在 :root 这个伪类中,这让该自定义属性能在全局可用(它匹配<html>元素内的所有内容)。然后,使用 var() 函数把 ID 为 foo 的容器的 background-color 设置为自定义属性的值,这时该容器就有了浅蓝的背景色。

除此之外,还可以把淡蓝色应用到多个HTML元素的其他颜色属性上,如 colorborder-color 等。方法很简单,就是通过 var(--my-cool-background) 获取自定义属性的值,然后给相应的CSS属性设置上去。(当然,我建议在事情变得混乱之前考虑一下CSS变量的命名约定):

p {
    color: var(--my-cool-background);
}

See the Pen by SitePoint () on .0

你也可以在CSS变量中使用另一个CSS变量,例如:

--top-color: orange;
--bottom-color: yellow;
--my-gradient: linear-gradient(var(--top-color), var(--bottom-color));

上面的代码创建了一个 --my-gradient 变量,是一个渐变样式,它的值是使用 --top-color--bottom-color 变量创建的一个渐变。现在,你可以在任何地方通过仅仅改变变量的值来修改渐变,而不必到处在样式表中创建渐变实例。

下面是一个在线CodePen演示。

See the Pen by SitePoint () on .0

最后,你可以在CSS变量中加入一个或多个备用值,例如:

var(--main-color, #333);    

上面的代码中,#333是一个备用值。如果未设置备用值,则在自定义属性无效或未设置的情况下,将应用继承的值。

CSS变量是区分大小写的

与普通CSS属性不同,CSS变量是区分大小写的。

例如,var(--foo)var(--FOO) 是获取两个不同的自定义属性(分别是 --foo--FOO)的值。

CSS变量受级联关系影响

和普通CSS属性一样,CSS变量是可继承的。例如,我们定义了一个属性,值为 blue :  

:root {
    --main-color: blue;
}

当你在 <html> 标签中的任意元素指定 --main-color 变量时,它们都会继承到blue这个值。

如果你在另一个元素里面给自定义属性赋了一个不同的值,这个元素的所有子元素就会继承这个新值,例如:  

:root {
    --main-color: blue;
}
.alert {
    --main-color: red;
}
p {
    color: var(--main-color);
}
<--! HTML -->
<html>
  <head>
    <!-- head code here -->
  </head>

  <body>
    <div>
      <p>blue 的段落</p>
      <div class="alert">
        <p>red 的段落</p>
      </div>
    </div>
  </body>
</html>

在上面的标签中,第一个段落会继承到全局的 --main-color 值,它是蓝色。

在div标签中拥有 .alert 类的段落会是红色,因为它的值继承自局部作用域里的 --main-color ,这个变量的值是 red

See the Pen by SitePoint () on .0

内联样式中的 CSS 变量

CSS变量也可以在元素的内联样式中定义。 假设您有一个可以控制大小的组件,

See the Pen by feiwen8772 () on .0

你可以看到元素的内联样式中同样可以定义CSS变量,而且同样遵循相同的级联规则。

var()函数

现在你知道了 var() 函数的用法。有关此功能的更多信息。 看下面的代码,有一个红色的 div 和一个绿色的div。只有一个CSS变量用于子绿色 div

See the Pen by feiwen8772 () on .0

现在从:root选择器 删除CSS变量 --background: green;,看看会发生什么。

您可能猜测子元素将具有从父元素继承的背景红色。 错了,这里有点特殊情况。 当您在任何CSS属性中使用该变量时,如果没有定义变量,那么它将默认采用默认值。 在这个例子中,背景颜色将是transparent(透明的):

See the Pen by feiwen8772 () on .0

无效的值

如果CSS变量有一个无效的值,比如 --background: blah blah blah; 或拼写错误 --background: yelow; /* yellow 拼写错误 */,那么 CSS 属性将默认采用默认值,如:

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

下面这个例子中,background的值为transparent,也就是说背景颜色是透明的。

See the Pen by feiwen8772 () on .0

回退值(fallback value)

有时可能会出现无法定义CSS变量的情况。在这种情况下,您可以将回退值设置为 var() 函数中的第二个参数

background: var(--background, green);

See the Pen by feiwen8772 () on .0

你也可以嵌套多个var() 函数 background: var(--color1, var(--color2, var(--color3, #00BCD4)));

结合 calc() 函数

如果您以前从未使用过它,那么我现在告诉你 calc() 函数是一个很实用的小工具,可以让您执行计算以确定CSS值。 它在所有现代浏览器上都得到了很好的支持,并且可以与 CSS变量结合使用,以构建新值。 下面的例子中 <div>width是动态计算的,

See the Pen by feiwen8772 () on .0

结合媒体查询 @media

您甚至可以在媒体查询中重新设置变量值,并让这些新值在任何地方使用它们级联,特别要说明的是:这是预处理器变量无法实现的。

查看此示例,其中媒体查询更改用于设置非常简单网格的变量,打开 codepen,然后尝试调整浏览器的大小,你可以看到媒体查询中的 CSS 变量依然有效。

See the Pen by feiwen8772 () on .0

现在了解这些规则足够,让我们来编码吧!

如何在SVG中使用CSS变量

CSS变量和SVG配合得很好。你可以使用CSS变量去修改SVG中的样式,以及和呈现相关的属性。

比如,你想通过SVG图标元素的父元素来给它一个不同的颜色。你可以在父元素内设置一个局部的CSS变量,然后把它赋值成你想要的颜色,然后,父元素内的图标就能从父元素继承到合适的颜色。

下面是相关代码:

/* 图标的内联SVG symbol */
<svg>
  <symbol id="close-icon" viewbox="0 0 200 200">
    <circle cx="96" cy="96" r="88" fill="none" stroke="var(--icon-color)" stroke-width="15" />
    <text x="100" y="160" fill="var(--icon-color)" text-anchor="middle" style="font-size:250px;">x</text>
  </symbol>
</svg>

/* 图标的第一个实例  */
<svg>
  <use xlink:href="#close-icon" />
</svg>

上面的代码使用了 <symbol> 标签,它让你创建一 SVG 图形的不可见的版本。然后再使用 <use> 标签生成一个可见的副本。这种方法可以让你根据自己的喜好创建任意多个自定义的图标,也就是通过它的ID( #close-icon )指向那个 <symbol> 。这比一遍又一遍地写重复的代码创建图形更加简便。如果你想提高这方便的技术,Massimo Cassandro在他的 中提供了一个快速教程。

注意 SVG中的圆形元素的 stroke 属性值和文本元素的 fill 属性值:它们都使用了一个CSS变量,--icon-color ,这个变量定义在CSS文档的 :root 选择器上,如下所示: 

:root {
    --icon-color: black;
}

这是当前图标看起来的样子:

如果你现在把SVG图标放到不同的容器中,然后在每个父元素的选择器中给这个变量赋不同的颜色值,你就能在不添加任何样式规则的情况下创建不同颜色的图标。这很酷!

为了展示这一点,我们把上面图标的一个实例放在一个有 .success 类的 div 中。

HTML 代码:

<!-- html -->
<div class="success">
    <svg>
        <use xlink:href="#close-icon" />
    </svg>
</div>

现在,让 --icon-color 变量局部化,即把它放在 .success 中,并设置一个 green 值。我们来看看发生的变化:

CSS 代码:

/* css */
.success {
    --icon-color: green;
}

这个图标的颜色就变成了绿色:  

查看下面完整的演示示例:

See the Pen by SitePoint () on .0

如何在@keyframes中使用CSS变量

CSS变量可以在CSS动画中使用,不论是在一般的HTML元素还是内联SVG元素上。只需要记得,你得知道让什么元素动,把它视为目标元素,然后创建对该目标元素的选择器,在选择器的作用范围中定义你的CSS变量,然后,使 var() 获取这些变量,把它们设置到 @keyframes 代码块中。

例如,让SVG中 .bubble 类里面的 <ellipse> 元素动起来,你的CSS可能会看起来像这样:

.bubble {
  --direction-y: 30px;
  --transparency: 0;
  animation: bubbling 3s forwards infinite;
}

@keyframes bubbling {
  0% {
    transform: translatey(var(--direction-y));
    opacity: var(--transparency);
  }
  40% {
    opacity: calc(var(--transparency) + 0.2);
  }
  70% {
    opacity: calc(var(--transparency) + 0.1);
  }
  100% {
    opacity: var(--transparency);
  }
}

注意到这是如何借助 ,并用 var() 函数进行计算的。它们增强了你代码的灵活性。

这个例子简洁的地方在于,利用CSS属性,你可以简单的修改相应选择器里变量值而调整动画,而不需要挨个去查找 @keyframes 里的属性了。

下面是完整的CodePen演示: 

See the Pen by SitePoint () on .0

如何通过JavaScript操作CSS变量

另一个超级酷的事情就是,你可以直接通过JavaScript代码访问CSS变量。通过 getComputedStylesetPropertygetPropertyValue从JavaScript访问CSS变量非常简单。 要获取变量,请使用 getPropertyValue()

假设在你的CSS文件中,有一个叫做 --left-pos 的变量,作用在 .sidebar 选择器中,值为 100px

.sidebar {
    --left-pos: 100px;
}

那么,使用类似下面的 JavaScript 代码获取 --left-pos 的值:

// 缓存你即将操纵的元素
const sidebarElement = document.querySelector('.sidebar');

// 缓存sidebarElement的样式于cssStyles中
const cssStyles = getComputedStyle(sidebarElement);

// 获取 --left-pos CSS变量的值
const cssVal = String(cssStyles.getPropertyValue('--left-pos')).trim();

// 将CSS 变量的值打印到控制台: 100px
console.log(cssVal);

使用类似下面的JavaScript代码给CSS变量赋值:

sidebarElement.style.setProperty('--left-pos', '200px');

上面的代码将sidebar元素中 --left-pos 变量的值设置为 200px

请看看CodePen中的如下示例,你可以交互式地点击侧边栏,修改 属性和背景色。这些实现只用到了CSS变量和JavaScript。

See the Pen by SitePoint () on .0

还有一些简单的方法,这里来看看不使用 getComputedStyle(),获取变量值:

/* 从 :root 根元素获取变量值 */
document.documentElement.style.getPropertyValue('--background');

/* 从 .block-3 元素获取变量值 */
document.querySelector('.block-3').style.getPropertyValue('--background');

修改变量值:

/* 修改 :root 根元素的变量值 */
document.documentElement.style.setProperty('--background', '#ff0000');

/* 修改 .block-3 元素的变量值 */
document.querySelector('.block-3').style.setProperty('--background', '#ff0000');

看一个简单的示例:

See the Pen by feiwen8772 () on .0

其他一些注意点

还有一些有趣的事情,在开发时候需要注意。

空值和空格

/* 无效的 */
--color:;

/* 有效的 */
--color: ; /* 值是空格 */

背景图片 url()

/* 无效的 - CSS 不支持拼接*/
.logo{
    --logo-url: 'logo';
    background: url('assets/img/' var(--logo-url) '.png');
}

/* 无效的 - CSS bug */
.logo{
    --logo-url: 'assets/img/logo.png';
    background: url(var(--logo-url));
}

/* 有效的 */
.logo{
    --logo-url: url('assets/img/logo.png');
    background: var(--logo-url);
}

单位相关

/* 无效的 */
--width: 10;
width: var(--width)px;

/* 有效的 */
--width: 10px;
width: var(--width);

/* 有效的 */
--width: 10;
width: calc(1px * var(--width)); /* 乘以1个单位进行转换 */
width: calc(1em * var(--width));

浏览器对CSS变量的支持情况

除了IE11(它不支持CSS变量),所有主流浏览器都。

对于不支持CSS变量的浏览器,一个变通的方案是使用具有虚拟查询条件(dummy conditional query)的 :

section {
    color: gray;
}

@supports(--css: variables) {
    section {
        --my-color: blue;
        color: var(--my-color, 'blue');
    }
}

因为IE/Edge支持 @supports ,所以上面的代码会生效。如果在var()函数中添加一个后备值,你的代码将会更加健壮,在支持的更加不好的浏览器中也能优雅降级。

所以,在Chrome和其他支持CSS变量的浏览器中,<section>元素内部的文本是蓝色的:

在IE11中,由于它不支持CSS变量,页面将显示灰色文本:

查看在线演示:

See the Pen by SitePoint () on .0

这种方式的缺点是如果你在项目中使用了大量的CSS变量,但是该项目主要通过不支持CSS变量的浏览器打开,那么代码不仅会变得有点儿复杂,维护也将会是噩梦。

在这种情况下,你可以选择使用支持 的PostCSS,然后你就可以编写尖端的CSS代码了,兼容不支持的浏览器交给PostCSS去做就可以了,这有点儿像JavaScript的编译器。如果你想了解PostCSS,SitePoint Premium为其所有成员提供了有关此主题的精彩。

了解了CSS自定义属性后,你可以看看具体的应用实例:【实例】通过 CSS自定义属性(CSS变量)和 JavaScript 实现高级CSS主题切换

资源

想了解更多CSS变量的细节,包括浏览器兼容问题的解决方案和有趣的例子,查看下面的资源:

  • — W3C Spec
  • — MDN
  • (video)
  • — Chris Coyier (CSS-Tricks)
  • — Serg Hospodarets (Smashing Magazine)
  • — Una Kravets
  • — Harry Roberts (CSS Wizardry)
  • — Amelia Bellamy-Royds (CodePen).

有趣的例子

  • — Wes Bos
  • — Wes Bos
  • — Chris Coyier
  • — Stephanie
  • .

你还在等什么?尝试使用CSS变量并通过评论让我知道你的想法!

本文主要内容来自: ,在原文基础上增加了很多需要注意的点。

赞(2) 打赏
未经允许不得转载:WEBTian开发 » CSS变量(自定义属性)实用指南及注意事项

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

Tian开发相关广告投放 更专业 更精准

联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏