使用 CSS 自定义属性(变量)

自定义属性(有时称为CSS 变量级联变量)是由 CSS 作者定义的实体,表示将在整个文档中重复使用的特定值。它们使用 @property At规则或 自定义属性语法(例如,--primary-color: blue;)进行设置。可以使用 CSS var() 函数(例如,color: var(--primary-color);)访问自定义属性。

复杂的网站拥有大量的 CSS,这通常会导致许多重复的 CSS 值。例如,在样式表中,同一个颜色经常在数百个不同的地方使用。更改在一个地方重复多次的颜色需要搜索替换所有规则和 CSS 文件。自定义属性允许在一个地方定义一个值,然后在多个其他地方引用它,以便于使用。另一个好处是可读性和语义。例如,--main-text-color 比十六进制颜色 #00ff00 更易于理解,尤其是在颜色用于不同上下文时。

使用两个连字符 (--) 定义的自定义属性受 级联 影响,并从其父级继承其值。@property At规则允许对自定义属性进行更多控制,并允许您指定它是否从父级继承其值,初始值是什么,以及应应用的类型约束。

注意:变量在媒体查询和容器查询中不起作用。您可以在任何元素上任何属性的值的任何部分使用 var() 函数。您不能将 var() 用于属性名称、选择器或属性值以外的任何内容,这意味着您不能在媒体查询或容器查询中使用它。

声明自定义属性

在 CSS 中,您可以使用两个连字符作为属性名称的前缀来声明自定义属性,或者使用 @property At规则。以下部分介绍了如何使用这两种方法。

使用两个连字符 (--) 作为前缀

以两个连字符开头的自定义属性以--开头,后跟属性名称(例如,--my-property),以及可以是任何有效 CSS 值的属性值。与任何其他属性一样,这都写在规则集中。以下示例展示了如何创建自定义属性--main-bg-color,并使用brown<named-color>

css
section {
  --main-bg-color: brown;
}

赋予规则集的选择器(上例中的<section>元素)定义了可以使用自定义属性的范围。出于这个原因,一种常见的做法是在:root伪类上定义自定义属性,以便可以在全局范围内引用它

css
:root {
  --main-bg-color: brown;
}

但这并非总是必须的:您可能有一些充分的理由来限制自定义属性的范围。

注意:自定义属性名称区分大小写——--my-color将被视为与--My-color不同的自定义属性。

使用@property规则

使用@property规则,您可以更具表达性地定义自定义属性,并能够将类型与属性关联,设置默认值以及控制继承。以下示例创建了一个名为--logo-color的自定义属性,它期望一个<color>

css
@property --logo-color {
  syntax: "<color>";
  inherits: false;
  initial-value: #c0ffee;
}

如果您想在 JavaScript 中而不是直接在 CSS 中定义或处理自定义属性,则可以使用相应的 API 来实现此目的。您可以在CSS 属性和值 API页面中了解有关其工作原理的信息。

使用var()引用自定义属性

无论您选择哪种方法来定义自定义属性,您都可以通过在标准属性值的前面引用var()函数中的属性来使用它们

css
details {
  background-color: var(--main-bg-color);
}

自定义属性入门

让我们从一些我们想要应用一些样式的 HTML 开始。有一个<div>充当容器,其中包含一些子元素,有些子元素嵌套了其他元素

html
<div class="container">
  <div class="one">
    <p>One</p>
  </div>
  <div class="two">
    <p>Two</p>
    <div class="three">
      <p>Three</p>
    </div>
  </div>
  <input class="four" placeholder="Four" />
  <textarea class="five">Five</textarea>
</div>

我们将使用以下 CSS 根据元素的类来设置一些不同元素的样式(下面未显示一些布局规则,以便我们可以专注于颜色)。根据它们的类,我们为元素提供了cornflowerblueaquamarine背景颜色

css
/* For each class, set some colors */
.one {
  background-color: cornflowerblue;
}
.two {
  color: black;
  background-color: aquamarine;
}
.three {
  background-color: cornflowerblue;
}
.four {
  background-color: cornflowerblue;
}
.five {
  background-color: cornflowerblue;
}

这产生了以下结果

有机会使用自定义属性来替换这些规则中重复的值。在.container范围内定义--main-bg-color并在多个地方引用其值后,更新后的样式如下所示

css
/* Define --main-bg-color here */
.container {
  --main-bg-color: cornflowerblue;
}

/* For each class, set some colors */
.one {
  background-color: var(--main-bg-color);
}
.two {
  color: black;
  background-color: aquamarine;
}
.three {
  background-color: var(--main-bg-color);
}
.four {
  background-color: var(--main-bg-color);
}
.five {
  background-color: var(--main-bg-color);
}

使用 :root 伪类

对于某些 CSS 声明,可以在级联中更高位置声明它,并让 CSS 继承解决此问题。对于非简单的项目,这并非总是可行。通过在:root伪类上声明自定义属性并在整个文档中根据需要使用它,CSS 作者可以减少重复的需要

css
/* Define --main-bg-color here */
:root {
  --main-bg-color: cornflowerblue;
}

/* For each class, set some colors */
.one {
  background-color: var(--main-bg-color);
}
.two {
  color: black;
  background-color: aquamarine;
}
.three {
  background-color: var(--main-bg-color);
}
.four {
  background-color: var(--main-bg-color);
}
.five {
  background-color: var(--main-bg-color);
}

这导致的结果与上一个示例相同,但允许对所需属性值(--main-bg-color: cornflowerblue;)进行规范声明,如果您以后想更改整个项目的该值,这非常有用。

自定义属性的继承

使用两个连字符--而不是@property定义的自定义属性始终继承其父级的值。以下示例演示了这一点

html
<div class="one">
  <p>One</p>
  <div class="two">
    <p>Two</p>
    <div class="three"><p>Three</p></div>
    <div class="four"><p>Four</p></div>
  </div>
</div>
css
div {
  background-color: var(--box-color);
}

.two {
  --box-color: cornflowerblue;
}

.three {
  --box-color: aquamarine;
}

根据继承,var(--box-color)的结果如下

  • class="one"无效值,这是以这种方式定义的自定义属性的默认值
  • class="two"cornflowerblue
  • class="three"aquamarine
  • class="four"cornflowerblue(从其父级继承)

上面示例展示的自定义属性的一个方面是,它们的行为与其他编程语言中的变量不完全相同。该值是在需要时计算的,而不是存储并在样式表的其他地方重复使用。例如,您不能设置属性的值并期望在兄弟元素的后代规则中检索该值。该属性仅针对匹配的选择器及其后代设置。

使用@property控制继承

@property规则允许您明确指定属性是否继承。以下示例使用@property规则创建自定义属性。继承被禁用,定义了<color>数据类型,并且初始值为cornflowerblue

父元素将--box-color设置为green的值,并使用--box-color作为其背景颜色的值。子元素也使用background-color: var(--box-color),如果启用了继承(或如果使用双破折号语法定义),我们预计它将具有green的颜色。

html
<div class="parent">
  <p>Parent element</p>
  <div class="child">
    <p>Child element with inheritance disabled for --box-color.</p>
  </div>
</div>
css
@property --box-color {
  syntax: "<color>";
  inherits: false;
  initial-value: cornflowerblue;
}

.parent {
  --box-color: green;
  background-color: var(--box-color);
}

.child {
  width: 80%;
  height: 40%;
  background-color: var(--box-color);
}

因为在规则中设置了inherits: false;,并且在.child范围内没有声明--box-color属性的值,所以使用cornflowerblue的初始值而不是从父级继承的green

自定义属性的回退值

您可以使用var()函数和@property规则的initial-value为自定义属性定义回退值。

注意:回退值不用于解决 CSS 自定义属性不受支持时的兼容性问题,因为在这种情况下,回退值将无济于事。回退涵盖了浏览器支持 CSS 自定义属性并能够在未定义或具有无效值的所需变量时使用不同值的情况。

var()函数中定义回退

使用var()函数,当给定变量尚未定义时,您可以定义多个回退值;这在使用自定义元素Shadow DOM时非常有用。

函数的第一个参数是自定义属性的名称。函数的第二个参数是可选的回退值,当引用的自定义属性无效时,将使用该值作为替换值。该函数接受两个参数,将第一个逗号后的所有内容都分配为第二个参数。如果第二个参数无效,则回退将失败。例如

css
.one {
  /* Red if --my-var is not defined */
  color: var(--my-var, red);
}

.two {
  /* pink if --my-var and --my-background are not defined */
  color: var(--my-var, var(--my-background, pink));
}

.three {
  /* Invalid: "--my-background, pink" */
  color: var(--my-var, --my-background, pink);
}

如上例第二个示例(var(--my-var, var(--my-background, pink)))所示,包含自定义属性作为回退是使用var()提供多个回退的正确方法。但是,您应该意识到此方法的性能影响,因为它需要更多时间来解析嵌套变量。

注意:回退的语法(如自定义属性的语法)允许使用逗号。例如,var(--foo, red, blue)定义了red, blue的回退——第一个逗号和函数末尾之间的任何内容都被视为回退值。

使用@property初始值进行回退

除了使用var()之外,@property规则中定义的initial-value也可以用作回退机制。事实上,我们在@property继承部分已经看到了这一点。

以下示例使用@property规则将--box-color的初始值设置为cornflowerblue。在规则集之后,我们希望将--box-color设置为aquamarine,但值名称中存在拼写错误。第三个<div>也是如此,我们为期望有效<color>的自定义属性使用了2rem2remaqumarine都是无效的颜色值,因此应用了cornflowerblue的初始值

css
@property --box-color {
  syntax: "<color>";
  initial-value: cornflowerblue;
  inherits: false;
}

.one {
  --box-color: aquamarine;
  background-color: var(--box-color);
}

.two {
  --box-color: aqumarine;
  background-color: var(--box-color);
}

.three {
  --box-color: 2rem;
  background-color: var(--box-color);
}

无效的自定义属性

每个 CSS 属性都可以分配一个定义好的值集。如果您尝试为属性分配一个在其有效值集之外的值,则该值被认为是无效的

当浏览器遇到常规 CSS 属性的无效值(例如,color属性的值为16px)时,它会丢弃该声明,并为元素分配它们在不存在该声明的情况下具有的值。在以下示例中,我们看到常规 CSS 声明无效时会发生什么;color: 16px;被丢弃,而先前的color: blue规则被应用

html
<p>This paragraph is initially black.</p>
css
p {
  color: blue;
}

p {
  /* oops, not a valid color */
  color: 16px;
}

但是,在解析自定义属性的值时,浏览器还不知道它们将在哪里使用,因此它必须将几乎所有值都视为有效。不幸的是,这些有效值可以通过var()函数表示法在可能没有意义的上下文中使用。属性和自定义变量可能导致无效的 CSS 语句,从而导致在计算时有效的概念。

当浏览器遇到无效的var()替换时,将使用该属性的初始值或继承值。此示例与上一个示例相同,只是我们使用自定义属性。

浏览器将--text-color的值替换为var(--text-color),但16px对于color不是有效属性值。替换后,该属性没有意义,因此浏览器分两步处理这种情况

  1. 检查属性color是否可继承。它是可继承的,但此<p>没有设置color属性的任何父级。因此,我们继续执行下一步。
  2. 将其值设置为其默认初始值,即黑色。
html
<p>This paragraph is initially black.</p>
css
:root {
  --text-color: 16px;
}

p {
  color: blue;
}

p {
  color: var(--text-color);
}

对于此类情况,@property规则可以通过允许定义属性的初始值来防止出现意外结果

html
<p>This paragraph is initially black.</p>
css
@property --text-color {
  syntax: "<color>";
  inherits: false;
  initial-value: cornflowerblue;
}

:root {
  --text-color: 16px;
}

p {
  color: blue;
}

p {
  color: var(--text-color);
}

JavaScript 中的值

要在 JavaScript 中使用自定义属性的值,就像使用标准属性一样。

js
// get variable from inline style
element.style.getPropertyValue("--my-var");

// get variable from wherever
getComputedStyle(element).getPropertyValue("--my-var");

// set variable on inline style
element.style.setProperty("--my-var", jsVar + 4);

另请参阅