发布于 2025-12-07 0 阅读
0

What no one told you about CSS Variables

关于 CSS 变量,没人告诉你的事

CSS 变量很棒,但是您对它们了解一切吗?

在这篇文章中,我将重点介绍一些鲜为人知的 CSS 变量怪癖。读完之后,你对它们的看法将焕然一新。


目录

  1. 小心!important
  2. 他们无法存储 URL
  3. 他们可以使无效值有效
  4. 它们可以无单位使用
  5. 它们可以动画化
  6. 它们无法存储inherit价值
  7. 它们可以是空的
  8. CSS 变量不是 C++ 变量
  9. 他们只在父母和孩子之间工作
  10. 它们可能有奇怪的语法

1)小心!important

使用!importantCSS 变量有点棘手,所以让我们从一个简单的例子开始:

p {
  --color:red!important;

  color:var(--color);
  color:blue;
}
Enter fullscreen mode Exit fullscreen mode

的颜色会是什么p?你认为这是red因为我们会有以下内容:

p {
  color:red!important;
  color:blue;
}
Enter fullscreen mode Exit fullscreen mode

但事实并非如此!的颜色p将是蓝色,因为我们将具有以下内容:

p {
  color:red;
  color:blue;
}
Enter fullscreen mode Exit fullscreen mode

!important在这种情况下, 不是 color 值的一部分,而是用来增加 的特殊性--color。从规范中可以看出:

注意:自定义属性可以包含尾随的 !important,但 CSS 解析器会自动将其从属性值中移除,从而使自定义属性在 CSS 级联中成为“important”。换句话说,禁止使用顶级“!”字符并不能阻止使用 !important,因为 !important 在语法检查之前就被移除了。

为了更好地理解,这里有另一个例子:

p{
  --color:red!important;
  --color:blue; 

  color:var(--color);
}
Enter fullscreen mode Exit fullscreen mode

上面会给我们一种red颜色:

  1. 我们有两个相同属性的声明,--color因此我们需要解决级联问题。第一个声明为 having !important,因此它胜出
  2. 我们有获胜者(--color:red!important),因此!important被移除,然后将值应用于color
  3. 我们有color:red。

让我们编写代码:

p{
  --color:red!important;
  --color:blue; 

  color:var(--color);
  color:blue;
}
Enter fullscreen mode Exit fullscreen mode

--color按照同样的逻辑,我们解决了和的级联color。--color:red!important是赢家,并且的也是赢家,color:blue所以最后我们得到了,blue因为我们不再关心了color:var(--color)。

一个重要的规则是始终将 CSS 变量(自定义属性)视为普通属性,而不仅仅是存储值的变量。

自定义属性是普通属性,因此可以在任何元素上声明,使用常规继承和级联规则进行解析,可以使用@media和其他条件规则进行条件设置,可以在HTML的style属性中使用,可以使用CSSOM进行读取或设置等。参考


2)它们无法存储 URL

这是一个你有一天会遇到的常见限制。

你不能做什么❌

:root {
  --url:"https://picsum.photos/id/1/200/300";
}
.box {
  background:url(var(--url));
} 
Enter fullscreen mode Exit fullscreen mode

你应该做什么✔️

:root {
  --url:url("https://picsum.photos/id/1/200/300");
}
.box {
  background:var(--url);
} 
Enter fullscreen mode Exit fullscreen mode

此限制与解析方式有关url()。解释起来有点棘手,但正如我们所见,修复起来相当简单。始终url()在 CSS 变量中添加该部分。

如果您想要更准确的细节,我建议您阅读这个Stack Overflow 答案


3)它们可以使无效值变得有效!

这是我最喜欢的怪癖,它会让你很头疼。

让我们从一个基本示例开始:

.box {
  background: red;
  background: linaer-gradient(red, blue);
}
Enter fullscreen mode Exit fullscreen mode

我们的.box将具有渐变色……等等,不,它有red背景。啊!我在 中打错了linear-*。我很容易注意到我的错误,因为浏览器跳过了声明并使用了前一个。

替代文本

现在,我们引入一个变量:

.box {
  --color:red;
  background: var(--color);
  background: linaer-gradient(var(--color), blue);
}
Enter fullscreen mode Exit fullscreen mode

测试代码,你会发现背景现在是透明的,第二个声明不再被划掉,因为它现在是有效的了。你甚至会注意到第一个声明被划掉了,因为第二个声明覆盖了它。

这里到底发生了什么事?!!

当在属性中使用变量时,浏览器只会在“计算值时”评估该属性的值,因为我们需要首先知道变量的内容。在这种情况下,浏览器在执行级联时会将该值视为有效,只有在之后才会变为无效。

在我们的例子中,浏览器在解析级联后会考虑最后一个声明。然后在执行评估时,它似乎无效,因此会被忽略。由于我们已经解析了级联,因此我们不会回到上一个声明,最终结果是没有背景,因此背景是透明的。

您可能认为这种行为不合逻辑,但它确实合乎逻辑,因为基于 CSS 变量,值可能有效或无效,所以浏览器从一开始就无法知道。

.box {
  --color:10px; /* a "valid" variable */
  background: red; /* a "valid" declaration */
  background:linear-gradient(var(--color),blue); /* a "valid" declaration that will override the first one  */
  /* The result is an "invalid" value ... */ 
}

Enter fullscreen mode Exit fullscreen mode

如果属性包含一个或多个 var() 函数,且这些函数在语法上有效,则必须假定整个属性的语法在解析时有效。只有在 var() 函数被替换后,才会在计算值时进行语法检查。ref

和

如果声明包含一个引用自定义属性及其初始值的 var() 函数(如上所述),或者声明使用了有效的自定义属性,但该属性值在替换其 var() 函数后无效,则该声明在计算值时可能无效。发生这种情况时,属性的计算值要么是其继承值,要么是其初始值,具体取决于该属性是否被继承,就像属性值被指定为 unset 关键字一样。ref

简单来说:CSS 变量会使属性处于待命状态,直到我们进行评估。只有在评估之后,我们才能判断它是有效还是无效。如果无效,那就太晚了,我们无法再使用另一个变量。

相关的Stack Overflow 问题


4)它们可以无单位使用

几乎所有的教程/课程都会向您展示这样的例子:

:root {
 --p: 10px;
}
.box {
  padding: var(--p);
}
Enter fullscreen mode Exit fullscreen mode

但您也可以执行以下操作:

:root {
 --p: 10;
}
.box {
  padding: calc(var(--p)*1px);
}
Enter fullscreen mode Exit fullscreen mode

变量中没有单位不是强制性的,在某些情况下,使用无单位的值甚至更好,因为添加单位相当容易,而且我们可能需要使用具有不同单位的相同值。

这是众多示例中的一个例子(取自这个答案)

永远不要忘记这个重要功能。它总有一天会帮你节省时间。


5)它们可以动画化

最初,CSS 变量根据规范被定义为不可动画的属性:

可动画化:否

但情况已经发生了变化,由于新功能,@property我们可以使用 CSS 变量进行动画/过渡。

支持仍然很低(特别是在 Firefox 上),但现在是时候了解这一点了。

以下是我依赖此功能的一些用例:

我会写更多文章来展示我们能用它创造的奇迹。敬请期待!


6)它们无法存储inherit价值

让我们考虑以下示例:

<div class="box">
  <div class="item"></div>
</div>
Enter fullscreen mode Exit fullscreen mode
.box {
  border:2px solid red;
}
.item {
  --b:inherit;
  border:var(--b);
}
Enter fullscreen mode Exit fullscreen mode

直观地,我们可能会认为.item会继承其父元素的相同边框,因为--bcontainsinherit但事实并非如此(你可以尝试看看)。

正如我在 (1) 中解释的那样,常见的错误是认为 CSS 变量只是存储一个我们以后可以使用的值,而不是实际存储的。CSS 变量(自定义属性)是普通属性,因此inherit 适用于 CSS 变量,而不是存储在 CSS 变量中。

例子:

.box {
  --b:5px solid blue; /* we define the variable on the parent */
}
.item {
  --b:inherit; /* the child will inherit the same value so "5px solid blue"*/
  border:var(--b); /* we will have "5px solid blue" */
}
Enter fullscreen mode Exit fullscreen mode

如您所见,继承的逻辑对它们的应用方式与对常见属性的适用方式相同。

值得注意的是,上述操作毫无意义,因为 CSS 变量默认是继承的。这就像设置inherit一个默认继承的属性一样(color例如)。

这就是说,我已经阐述了一种能够使用 CSS 变量的技术inherit

为了说明这个问题,我们来考虑这个简化的例子:

:root {
  --color:rgba(20,20,20,0.5); /*defined as the default value*/
}

.box {
  width:50px;
  height:50px;
  display:inline-block;
  margin-right:30px;
  border-radius:50%;
  position:relative;
}
.red {background:rgba(255,0,0,0.5);}
.blue {background:rgba(0,255,0,0.5);}

.box:before{
  content:"";
  position:absolute;
  top:0;left:0;right:0;bottom:0;
  border-radius:50%;
  transform:translateX(30px);
  background:var(--color);
  filter:invert(1);
}
<!-- we can
…
</p>
Enter fullscreen mode Exit fullscreen mode



同样的逻辑也适用于其他关键字,如unset:revert如何将 CSS 变量设置为未设置的值,“--unset-it: unset”?


7)它们可以为空

是的,您可以执行以下操作:

.box {
  --color: ;
  background:var(--color); 
}
Enter fullscreen mode Exit fullscreen mode

根据规范,以上内容有效:

注意:虽然<declaration-value>必须至少表示一个标记,但该标记可以是空格。这意味着--foo: ;是有效的,并且相应的var(--foo)调用将以单个空格作为其替换值,但这--foo:;是无效的。

注意最后一句,因为我们至少需要一个空格。下面的代码是无效的:

.box {
  --color:;
  background:var(--color); 
}
Enter fullscreen mode Exit fullscreen mode

替代文本

这个怪癖主要与后备功能一起使用来做一些神奇的事情。

理解这个技巧的一个基本例子:

.box {
  background:
   linear-gradient(blue,transparent)
   var(--color,red); 
}
Enter fullscreen mode Exit fullscreen mode
<div class="box">
  I will have `background:linear-gradient(blue,transparent) red;`
</div>
<div class="box" style="--color:green">
  I will have `background:linear-gradient(blue,transparent) green;`
</div>
<div class="box" style="--color: ;">
  I will have `background:linear-gradient(blue,transparent)  ;`
</div>
Enter fullscreen mode Exit fullscreen mode
  1. 第一个框没有定义变量,因此将使用后备。
  2. 第二个定义了一个变量,因此它将被使用
  3. 最后一个定义了一个空变量,这样就可以使用空值了。就好像我们不再需要了var(--color,red)。

空值允许我们从属性中删除声明!这在复杂值中var()使用时非常有用。var()

如果var()单独使用,则适用相同的逻辑,但最终会得到一个对于大多数属性无效的空值。

如果我们采用第一个例子,我们将得到background: ;在“计算值时间”导致无效值(记住(3))的透明背景。


8)CSS 变量不是 C++ 变量

不幸的是,许多开发人员倾向于将 CSS 变量与其他语言的变量进行比较,最终导致逻辑上出现很多问题。出于这个原因,我不想将它们称为“变量”,而是“自定义属性”,因为它们本身就是属性。

每个人都想做的事情

:root {
  --p:5px;
  --p:calc(var(--p) + 1px); /* let's increment by 1px */
}
Enter fullscreen mode Exit fullscreen mode
:root {
  --x: 5px;
  --y: 10px;
  /* let's do a variable switch */
  --c: var(--y);
  --y: var(--x);
  --x: var(--c);
}
Enter fullscreen mode Exit fullscreen mode
.box {
  --s: 10px;
  margin:var(--s); /* I want 10px of margin */
  --s: 20px;
  padding:var(--s): /* then 20px of padding */
}
Enter fullscreen mode Exit fullscreen mode

以上所有方法都行不通。前两个方法根本无效,因为我们有一个循环依赖关系,因为一个变量引用了自身(第一个例子)或一组变量(第二个例子),从而形成了循环。

在最后一个例子中,和都padding将margin具有20px,因为级联将优先考虑--s: 20px应用于margin和的最后一个声明padding。

这就是说,在使用 CSS 变量时,您应该停止考虑 C++、JavaScript、Java 等,因为它们是具有其逻辑的自定义属性。


9)他们只在父母和孩子之间工作。

记住这条黄金法则:CSS 变量总是从父元素(或祖先元素)传递到子元素。它们永远不会从子元素传递到父元素,也不会在兄弟元素之间传递。

这会导致我们犯以下错误:

:root {
  --c1: red;
  --c2: blue;
  --grad: linear-gradient(var(--c1),var(--c2);
}
.box {
  --c1: green;
  background:var(--grad);
}
Enter fullscreen mode Exit fullscreen mode

你认为的背景.box会是 吗linear-gradient(green, blue)?不,会是linear-gradient(red, blue)。

根元素是 DOM 中最上层的元素,因此它是我们box元素的祖先,而我们的黄金法则规定我们只能执行父级 --> 子级,因此--c1不能朝相反的方向到达根元素,进行更改--grad,然后我们再回到另一个方向重新发送更改后的值--grad。

在这个例子中,.box会继承 的值,该值由和inside--grad的值定义。更改只会改变inside的值,仅此而已。--c1--c2:root--c1--c1.box

以下是我围绕这个主题写的更详细的答案:

我正在尝试通过自定义属性缩放尺寸var,这样类就可以组合在一起而不耦合。期望的效果是这 3 个列表将采用三种不同的缩放比例,但正如CodePen 上演示的那样,这 3 个列表都采用相同的缩放比例。我正在寻找……

甚至Stack Overflow 团队也偶然发现了这个怪癖!


10)它们可能有奇怪的语法

最后一个有趣的怪癖。

您知道您可以执行以下操作吗?以下内容不再有效。规范已更新,禁止使用此类内容。

body {
  --:red;
  background:var(--);
}
Enter fullscreen mode Exit fullscreen mode

太神奇了吧?没错,只用两个破折号就能定义 CSS 变量。相关:"--" 是有效的 CSS3 标识符吗?

你认为上面的内容很疯狂,请看一下下面的内容:

body {
 --📕:red;
 --📗:green; 
 --📘:blue;
 --📙:orange;
}
Enter fullscreen mode Exit fullscreen mode

是的,表情符号!您可以使用表情符号定义变量,而且它可以起作用。

CSS 变量的语法几乎允许所有情况,唯一的要求是以 开头--。也可以以数字开头(例如:--1:)。相关:CSS 变量名可以以数字开头吗?

为什么不只使用破折号:

body {
  ---------:red;
  background:var(---------);
}
Enter fullscreen mode Exit fullscreen mode

或者同一个变量存储两个不同的值

body {
  --‎​:red;
  --‎:blue;
  background:linear-gradient(90deg, var(--‎​),var(--‎));
}
Enter fullscreen mode Exit fullscreen mode

尝试上述方法,您将获得渐变色彩!

为了实现这种神奇的效果,我使用了隐藏字符,使得两个变量看起来不同,但实际上它们看起来是一样的。如果你在jsfiddle.net上尝试一下这段代码,你会看到以下内容:

替代文本

当然,你永远不应该在实际项目中使用这样的东西,除非你想让你的老板和同事抓狂。


就是这样

我知道这些信息一下子会很多,但你不必记住所有东西。我尝试将最不为人知和最不直观的行为归类到 CSS 变量周围。如果有一天某些功能没有按预期工作,请回到这里。你很可能在上面找到答案。

最后,我将以一些我回答过的、可能有用的 Stack Overflow 问题作为结尾:

如何在 calc() 表达式中获取 CSS 变量的负值?

如何使用类似于 SASS 的 darken() 的 CSS 变量创建颜色阴影?

如何使用 calc() 在颜色值之间切换?

递归变量可以在css中表达吗?

获取使用类似 calc 的表达式的 CSS 变量的计算值

当单选按钮的选中选择器被触发时,CSS 变量是否可能发生改变?


给我买杯咖啡

或者

成为赞助人

文章来源:https://dev.to/afif/what-no-one-told-you-about-css-variables-553o