Menu

CSS 变量:自定义属性、:root 与 var()

CSS 变量(自定义属性)把一个值存一次,再用 var() 反复使用。本文讲解如何在 :root 中声明变量、限定作用域并覆盖它们、提供后备值、用 JavaScript 切换主题,以及如何避开无效值陷阱。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

声明和使用 CSS 变量

CSS 变量的正式名称是自定义属性,它是名称以 -- 开头的属性。只需声明一次,通常写在 :root 上,让整个页面继承它,然后在任何地方用 var() 读取。

把 --brand 改成 #16a34a,卡片边框和按钮会一起变化。这正是变量的意义:一个值,只需在一个地方修改。变量还可以和 calc() 组合使用,按钮的左右内边距就是例子。

语法

:root {
  --name: value;         /* declare */
}

.element {
  color: var(--name);                 /* use */
  color: var(--name, #111827);        /* use, with a fallback */
  padding: calc(var(--space) * 2);    /* compute with it */
}
规则说明
名称以 -- 开头,后面跟字母、数字、- 或 _。区分大小写:--Brand 不等于 --brand。
值几乎可以是任何东西:颜色、长度、列表、字符串。在被使用之前不会检查。
在哪里声明任何规则中。全局值写在 :root,局部值写在组件的选择器里。
继承自定义属性会继承,所以子元素能看到祖先元素的值。
var() 能用在哪里只能用在属性值中,不能用在选择器、属性名或 @media 条件里。

:root 匹配 <html> 元素。用它而不用 html,是因为它的优先级和类相同,而且按照惯例,它表示“全局设置”。

作用域:按组件覆盖变量

因为变量会继承,在某个元素上重新声明变量,就会改变这个元素及其内部所有内容中的值。使用变量的那条规则保持不变。

各个变体只设置了一个变量,从不直接设置边框。再加第四个变体也只需一行。

后备值

当元素及其所有祖先都没有定义变量时,var(--name, fallback) 会使用后备值。第一个逗号之后的所有内容都是后备值,所以它本身可以包含逗号(var(--font, Georgia, serif)),也可以是另一个 var()。

用 JavaScript 修改变量:主题切换

JavaScript 可以用 style.setProperty() 在任何元素上设置变量。设置在 document.documentElement(即 <html> 元素)上,整个页面都会跟着变。更常见的做法是切换一个重新定义变量的类名:

用 getComputedStyle(element).getPropertyValue('--text') 读回变量的值。注意,setProperty 会在 <html> 上写入行内样式,而行内样式会胜过 .dark 类,所以按下第二个按钮之后,文字颜色在两种主题下都不会再变,直到用 root.style.removeProperty('--text') 移除这个行内值。

从 HTML 传值

把变量写在 style 属性里,是把每个元素各自的数据传给样式表最干净的方式。设计仍然留在 CSS 中,HTML 只提供一个数字。

calc(var(--value) * 1%) 把普通数字变成百分比。变量还可以在媒体查询中改变:@media (min-width: 900px) { :root { --space: 20px; } } 会让所有读取 --space 的间距在宽屏上变大。

无效值陷阱

浏览器无法提前知道变量的值对使用它的属性是否有效。如果无效,这个属性不会退回到规则中之前的声明,而是变成计算值时无效,表现得就像被设成了 unset:可继承的属性取父元素的值,其他属性取初始值。

文字从 <body> 继承了 16px,border-width 回到它的初始值 medium,浏览器会把它画成 3px。不用变量时,无效的声明会被丢弃,它前面的声明仍然有效;用了变量,就来不及这样处理了。当变量已定义但值不对时,后备值也帮不上忙:后备值只用于未定义的变量。

CSS 变量与 Sass 变量

CSS 变量Sass 变量
语法--brand: #2563eb; 和 var(--brand)$brand: #2563eb;
何时解析在浏览器中实时解析构建时解析,输出中不再存在
按元素改变可以,通过继承不可以
用类名或媒体查询改变可以不可以
用 JavaScript 改变可以不可以
用在选择器或媒体条件中不可以可以

两者可以配合使用:很多项目用 Sass 处理构建时的逻辑,再为所有需要主题化的内容输出 CSS 变量。

常见错误

  • 忘了两个连字符。var(brand),以及把 --brand 写成 -brand 来声明,都不起作用。
  • 在媒体查询中使用变量。@media (min-width: var(--bp)) 是无效的。媒体条件需要字面值。
  • 以为前面的声明能在变量无效时保留下来。不会保留;见上面的陷阱。
  • 单位混用出错。先写 --gap: 10;,再写 margin: var(--gap)px,得不到 10px。使用 calc(var(--gap) * 1px),或者直接存 10px。
  • 在错误的元素上声明。在 .card 上声明的变量,它的兄弟元素和父元素都看不到;共享的值要声明在 :root 上。

常见问题

如何声明 CSS 变量?

在规则里写一个名称以两个连字符开头的属性,全局值通常写在 :root 中::root { --brand: #2563eb; }。然后在任何地方用 var(--brand) 读取它,例如 color: var(--brand);。

CSS 中的 :root 是什么?

:root 是一个伪类,匹配文档的根元素,在 HTML 页面中就是 <html>。在这里声明的变量会被每个元素继承。它的优先级和类相同,所以会胜过普通的 html 选择器。

如何给 CSS 变量设置默认值?

给 var() 传入第二个参数:color: var(--accent, #2563eb);。当元素及其祖先都没有定义 --accent 时,就会使用这个后备值。如果变量已定义,但它的值对该属性无效,后备值不会生效。

如何用 JavaScript 修改 CSS 变量?

在元素的 style 上调用 setProperty:document.documentElement.style.setProperty('--brand', '#16a34a')。所有使用 var(--brand) 的规则会立刻更新。用 getComputedStyle(el).getPropertyValue('--brand') 读取当前值。

CSS 变量和 Sass 变量有什么区别?

Sass 变量在样式表编译时就被替换成了值,所以无法在浏览器中改变。CSS 变量存在于浏览器中:它们会继承,可以因元素而异,能响应媒体查询和类名,还能被 JavaScript 修改。

CSS 变量名区分大小写吗?

区分。--Brand 和 --brand 是两个不同的变量,这和 color 这类普通属性名不同,普通属性名不区分大小写。

Coddy 编程语言插图

和 Coddy 一起学编程

开始