CSS变量在父元素和子元素中的继承和覆盖规则?

css变量问题:父元素的一个变量A的值取决于该元素上定义的的另一个变量B,如果我在其子元素上重新定义一个B,并使用A做为某个属性的值,那么此时A的值是等于父元素上的变量B的值呢?还是重新定义的这个B的值呢?

<div class="container">
      <div class="A"></div>
      <div class="B"></div>
    </div>
.container {
  --light-color: red;
  --dark-color: green;
  --color: var(--light-color);
}
.A, .B {
  height: 100px;
}
.A {
  background-color: var(--color);
}
.B {
  --light-color: blue;
  background-color: var(--color);
}

我想为这个组件提供两种风格,浅色和暗色,故定义了两个环境变量

--light-color: red;
--dark-color: green;

但是我想为它的子组件提供不一样的浅色风格,于是重新定义了

--light-color: blue;

最终的颜色却没有向我所想的那样,还是父元素上定义的颜色。当我检查该元素的样式时,发现父元素上的--light-color变量是被划掉的,应用的应该是子元素上的--light-color,与事实不符,这可能是开发者工具的一个bug吧。
codesandbox
image.png

为什么没有改变呢?

猜测:
.container元素上的变量有

--light-color: red;
--dark-color: green;
--color: var(--light-color);

--color在计算的时候只会从当前节点及其祖先节点中找这个--light-color,所以子元素中的--light-color它是看不到的。

子元素.B上的变量有

--light-color

它需要用到--color,但是自己没有,就去父元素上找。所以还是这里的--light-color并没有改变.B的颜色。

之前的想法,认为这样是可行的。是我们在考虑.B时,将它继承的和自身定义的都集中在一起了,后定义的覆盖掉了前面的,之后再来决定.B的变量取值,这是我们在思考css时通常的思考方式,但在这里不可行。

阅读 1.7k
avatarAI BotBETA

CSS变量的继承和覆盖规则如下:

  1. 继承规则:CSS变量(也称为自定义属性)可以被子元素继承。如果子元素没有定义某个变量,那么它将继承其父元素的该变量值。
  2. 覆盖规则:如果子元素重新定义了某个变量,那么它将使用自己的变量值,而不是父元素的变量值。在这种情况下,子元素的变量值会覆盖父元素的变量值。

在你提供的示例中,.container元素定义了三个变量:--light-color--dark-color--color。其中,--color变量的值依赖于--light-color变量的值。

在子元素.B中,你重新定义了--light-color变量,并将其值设置为蓝色。然后,你尝试使用var(--color)来设置背景颜色。但是,由于.B元素没有定义--color变量,它将继承父元素.container中的--color变量。因此,背景颜色将根据父元素的--light-color变量值(即红色)计算得出。

如果你想在子元素中改变浅色风格的颜色,你可以直接在子元素中重新定义--color变量,而不是只重新定义--light-color变量。这样,子元素将使用自己的变量值来计算背景颜色。

这是修改后的CSS代码示例:

.container {
  --light-color: red;
  --dark-color: green;
  --color: var(--light-color);
}
.A, .B {
  height: 100px;
}
.A {
  background-color: var(--color);
}
.B {
  --light-color: blue;
  --color: var(--light-color); /* 重新定义 --color 变量 */
  background-color: var(--color); /* 使用重新定义的 --color 变量 */
}

这样,子元素.B的背景颜色将根据自己定义的--light-color变量值(即蓝色)计算得出,而不是继承父元素的变量值。

3 个回答

不是 BUG ,控制台划掉的只是因为 --light-color 变量被覆盖了,跟 --color 变量的值无关,所以 --color 还是在 .container 中赋值的 --light-color ,即 red,你点一下那个高亮还会有辅助指向

发生这个问题的核心是:css 变量是基于 重新定义 来实现覆盖的,所以它 没办法改变变量值

内部作用域覆盖,只是 让变量先找内部作用域,而 不是 修改外部已经定义的变量值

因此上述代码可以这样改(省略掉中间变量 color):

.container {
  --light-color: red;
  --dark-color: green;
}    

.A,
.B {
  height: 100px;
}

.A {
  background-color: var(--light-color);
}

.B {
  /* 这样才会正确覆盖作用域 */
  --light-color: blue;
  background-color: var(--light-color);
}

css 变量的作用域是可以覆盖,但是如果这个变量的值是另一个变量,在 var 的时候,其实已经是一个字符串了,不是引用了,所以再怎么改 .B 的变量,也不会生效,除非你重新定义一遍(当然这样没啥意义,不如去掉)

.B {
  --light-color: blue;
  --color: var(--light-color); /* 等于说是把外部的 --color 覆盖了 */
  background-color: var(--color);
}

参考规范:CSS Custom Properties for Cascading Variables Module Level 1

If a custom property is declared multiple times, the standard cascade rules help resolve it. Variables always draw from the computed value of the associated custom property on the same element:

译:如果一个自定义属性被多次声明,标准的级联规则可以帮助解决。变量总是从同一元素上关联自定义属性的计算值中获取。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题