自定义属性中的重大问题

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您的旅程的每个阶段提供云产品。立即开始使用 $200 免费积分!

我最近看到这个问题困扰了不止几个人,包括我自己,所以我要把它记录下来。

让我们将一些自定义属性添加到 CSS 中

html {
  --color-1: red;
  --color-2: blue;
}

让我们立即使用它们来创建一个背景渐变

html {
  --color-1: red;
  --color-2: blue;

  --bg: linear-gradient(to right, var(--color-1), var(--color-2));
}

现在假设页面上有几个 div

<div></div>
<div class="variation"></div>

让我对它们进行样式设置

div {
  background: var(--bg);
}

这完全有效!太棒了!

现在让我对那个变化进行样式设置。我不想让它从红色变为蓝色,而是想让它从绿色变为蓝色。很简单,我会将红色更新为绿色

html {
  --color-1: red;
  --color-2: blue;

  --bg: linear-gradient(to right, var(--color-1), var(--color-2));
}
div {
  background: var(--bg);
}
.variation {
  --color-1: green;
}

不行!(警报声大作,喇叭声喧嚣,家畜躲避起来)。

朋友们,这行不通。

据我所知,问题在于 --bg 从未在任何一个 div 上声明过。它可以使用 --bg,因为它是在更高级别上声明的,但在它被使用时,它的值就被锁定了。仅仅因为您更改了 --bg 在声明时碰巧使用的某个其他属性,并不意味着该属性会出去搜索它被使用的所有位置并更新所有依赖它的东西。

哎,这个解释不太对。但我只能想到这些。

解决方案?其实有很多。

解决方案 1:将变量作用域限定在您使用它的位置。

您可以这样做

html {
  --color-1: red;
  --color-2: blue;
}

div {
  --bg: linear-gradient(to right, var(--color-1), var(--color-2));
  background: var(--bg);
}
.variant {
  --color-1: green;
}

现在,--bg 已在两个 div 上声明,对 --color-1 依赖项的更改确实有效。

解决方案 2:用逗号分隔您设置大多数变量的选择器。

假设您做了常见的操作,即在 :root 上设置了许多变量。然后您就会遇到这个问题。您只需在该主要声明中添加其他选择器,以确保您命中了正确的范围。

html,
div {
  --color-1: red;
  --color-2: blue;

  --bg: linear-gradient(to right, var(--color-1), var(--color-2));
}
div {
  background: var(--bg);
}
.variation {
  --color-1: green;
}

在其他可能不太牵强的示例中,它可能看起来像这样

:root, 
.button,
.whatever-it-is-a-bandaid {
  --padding-inline: 1rem;
  --padding-block: 1rem;
  --padding: var(--padding-block) var(--padding-inline);
}

.button {
  padding: var(--padding);
}
.button.less-wide {
  --padding-inline: 0.5rem;
}

解决方案 3:覆盖模式

去它的吧——把变量放在任何地方。

* {
  --access: me;
  --whereever: you;
  --want: to;

  --hogwild: var(--access) var(--whereever);
}

这不是一个好计划。我最近听到有人在聊天中说,一个中等规模的网站的页面渲染延迟了 500 毫秒,因为页面的每次绘制都需要计算所有属性。它“有效”,但它属于少数情况下,您可以在选择器中导致真正的性能问题。

解决方案 4:引入新的“默认”属性和回退

此处所有功劳归属于 Stephen Shaw,他是 所有这些问题的探索者,我最初就是在那里看到这个混淆的。

让我们回到我们对这个问题的第一个演示

html {
  --color-1: red;
  --color-2: blue;

  --bg: linear-gradient(to right, var(--color-1), var(--color-2));
}

我们要做的是给自己两样东西

  1. 一种覆盖整个背景的方法
  2. 一种覆盖渐变背景部分的方法

所以我们将用这种方法来做

html {
  --color-1: red;
  --color-2: blue;
}
div {
  --bg-default: linear-gradient(to right, var(--color-1), var(--color-2));
  background: var(--bg, var(--bg-default));
}

请注意,我们根本没有声明 --bg。它只是在那里等待一个值,如果它获得了值,那么该值就是“获胜”的值。但是如果没有值,它将回退到我们的 --bg-default。现在…

  1. 如果我设置 --color-1--color-2,它将按预期替换渐变的对应部分(只要我在影响其中一个 div 的选择器上执行此操作)。
  2. 或者,我可以将 --bg 设置为将整个背景重置为我想要的任何内容。

感觉是一种处理事物的好方法。


有时,确实存在关于 CSS 自定义属性的错误。但这并不是其中之一。尽管对我来说这有点像错误,但它似乎不是。只是您需要了解的事情之一。