自定义属性的大问题

43 阅读3分钟

我看到最近有很多人对此感到困惑,包括我自己,所以我想把它写下来。

让我们在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);
}

这不是一个好计划,我最近无意中听到一个聊天,一个中等规模的网站经历了500ms的页面渲染延迟,因为每次绘制页面都需要计算所有的属性。这是 "有效的",但这是一个罕见的情况,你可以用选择器造成合法的性能问题。

解决方案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 ,将整个背景重置为我想要的样子。

感觉这是个不错的处理方式。