用于计算外部作用域中的变量时忽略 CSS 作用域自定义属性 [英] CSS scoped custom property ignored when used to calculate variable in outer scope

查看:32
本文介绍了用于计算外部作用域中的变量时忽略 CSS 作用域自定义属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过 var 自定义属性以类可以组合而不耦合的方式缩放大小.预期的效果是这 3 个列表将在 3 个不同的范围内,但作为 在 CodePen 上展示 全部 3列表是相同的比例.我正在寻找对作用域的解释和 CSS 自定义属性技术,该技术可以通过可组合的松散耦合代码实现这一点.

:root {--size-1: calc(1 * var(--scale, 1) * 1rem);--size-2: calc(2 * var(--scale, 1) * 1rem);--size-3: calc(3 * var(--scale, 1) * 1rem);}.size-1 { 字体大小:var(--size-1) }.size-2 { 字体大小:var(--size-2) }.size-3 { 字体大小:var(--size-3) }.scale-1x { --scale: 1 }.scale-2x { --scale: 2 }.scale-3x { --scale: 3 }html {字体:1em sans-serif;背景:木瓜;}奥尔{向左飘浮;列表样式:无;保证金:1rem;}

    <li class="size-1">size 1</li><li class="size-2">size 2</li><li class="size-3">size 3</li></ol><ol class="scale-2x"><li class="size-1">size 1</li><li class="size-2">size 2</li><li class="size-3">size 3</li></ol><ol class="scale-3x"><li class="size-1">size 1</li><li class="size-2">size 2</li><li class="size-3">size 3</li>

解决方案

在您的情况下,您已经评估了根级别的 --scale 自定义属性以定义 --size-* 属性,然后在子元素中定义 --scale 再次.这不会再次触发评估,因为它已经在上层中完成.

这里有一个简单的例子来说明这个问题:

.box {--color: var(--c, blue);}跨度 {颜色:var(--color);}

<div class="box"><!-- --c 在这个级别被评估--><span style="--c:red">我不会是红色的,因为已经评估了属性并且--color使用默认值设置为蓝色</span>

<div style="--c:red"><div class="box"><!-- --c 在这个级别被评估--><span>我会是红色的,因为在求值的时候--c是红色的(继承自上面的div)</span>

要解决您的问题,您需要将声明从 :root 移动到与 --scale 定义相同的级别:

.scale {--size-1: calc(1 * var(--scale, 1) * 1rem);--size-2: calc(2 * var(--scale, 1) * 1rem);--size-3: calc(3 * var(--scale, 1) * 1rem);}.size-1 { 字体大小:var(--size-1) }.size-2 { 字体大小:var(--size-2) }.size-3 { 字体大小:var(--size-3) }.scale-1x { --scale: 1 }.scale-2x { --scale: 2 }.scale-3x { --scale: 3 }html {字体:1em sans-serif;背景:木瓜;}奥尔{向左飘浮;列表样式:无;保证金:1rem;}

    <li class="size-1">size 1</li><li class="size-2">size 2</li><li class="size-3">size 3</li></ol><ol class="scale-2x scale"><li class="size-1">size 1</li><li class="size-2">size 2</li><li class="size-3">size 3</li></ol><ol class="scale-3x scale"><li class="size-1">size 1</li><li class="size-2">size 2</li><li class="size-3">size 3</li>

在这种情况下,--scale 定义在与其评估相同的级别,因此 --size-* 将为每种情况正确定义.


来自规范:

<块引用>

在属性值中替换 var():

  1. 如果自定义属性由 var() 的第一个参数命名函数被动画污染,并且 var() 函数被用于动画属性或它的一个好手,对待自定义属性具有此算法其余部分的初始值.
  2. 如果由第一个参数命名的自定义属性的值var() 函数不是初始值,替换 var()函数由相应的自定义属性的值.否则,
  3. 如果 var() 函数有一个回退值作为它的第二个参数,用回退值替换 var() 函数.如果有任何回退中的 var() 引用,也替换它们.
  4. 否则,包含 var() 函数的属性在计算值时间

在您的第一种情况下,您陷入了 3,因为在根级别没有为 --scale 指定值.在最后一种情况下,我们陷入了 2,因为我们在同一级别定义了 --scale 并且我们有它的价值.


在所有情况下,我们应该避免在 :root 级别进行任何评估,因为它根本没用.根级别是 DOM 中的最高级别,因此所有元素都将继承相同的值,除非我们再次评估变量,否则 DOM 中不可能有不同的值.

你的代码相当于这个:

:root {--size-1: calc(1 * 1 * 1rem);--size-2: calc(2 * 1 * 1rem);--size-3: calc(3 * 1 * 1rem);}


再举一个例子:

:root {--r:0;--g:0;--b:255;--color:rgb(var(--r),var(--g),var(--b))}div {颜色:var(--color);}p{--g:100;颜色:var(--color);}

一些文字

<p>一些文字

直觉上,我们可能认为我们可以通过更改在 :root 级别定义的 3 个变量之一来更改 --color 但我们不能这样做和以上代码和这个一样:

:root {--color:rgb(0,0,255)}div {颜色:var(--color);}p{--g:100;颜色:var(--color);}

一些文字

<p>一些文字

3 个变量(--r--g--b)在 :root 中求值code> 所以我们已经用它们的值替换了它们.

在这种情况下,我们有 3 种可能性:

  • 使用 JS 或其他 CSS 规则更改 :root 中的变量.这将不允许我们有不同的颜色:

:root {--r:0;--g:0;--b:255;--color:rgb(var(--r),var(--g),var(--b))}div {颜色:var(--color);}p{--g:200;/*这不会有任何影响!*/颜色:var(--color);}:根 {--g:200;/*这会起作用*/}

一些文字

<p>一些文字

  • 再次评估所需元素内的变量.在这种情况下,我们将失去任何灵活性,并且 :root 中的定义将变得无用(或至少将成为默认值):

:root {--r:0;--g:0;--b:255;--color:rgb(var(--r),var(--g),var(--b))}div {颜色:var(--color);}p{--g:200;--color:rgb(var(--r),var(--g),var(--b));颜色:var(--color);}

一些文字

<p>一些文字

  • 使用通用选择器 * 更改 :root 选择器.这将确保我们的功能在所有级别都得到定义和评估.在一些复杂的情况下,这可能会产生一些不需要的结果

* {--r:0;--g:0;--b:255;--color:rgb(var(--r),var(--g),var(--b))}div {颜色:var(--color);}p{--g:200;颜色:var(--color);}

一些文字

<p>一些文字


考虑到这一点,我们应该始终将评估保持在 DOM 树中可能的最低点,尤其是在变量更改之后(或在同一级别)

这是我们不应该做的

:root {--r: 0;--g: 0;--b: 0;}.颜色 {--color: rgb(var(--r), var(--g), var(--b))}.绿色 {--g: 255;}.红色的 {--r: 255;}p{颜色:var(--color);}h1{边框底部:1px 实心 var(--color);}

<h1 class="red">红色 </h1><p class="red">我想变红:(</p>

<div class="颜色"><h1 class="green">Green </h1><p class="green">我想变绿:(</p>

这是我们应该做的

:root {--r:0;--g:0;--b:0;}.颜色 {--color:rgb(var(--r),var(--g),var(--b));}.绿色 {--g:255;}.红色的 {--r:255;}p{颜色:var(--color);}h1{边框底部:1px 实心 var(--color);}

<h1 class="color">红色标题</h1><p class="color">是的,我是红色的 :D</p>

<div class="green"><h1 class="color">绿色标题</h1><p class="color">是的,我是绿色的 :D</p>

我们也可以这样做:

:root {--r:0;--g:0;--b:0;}.颜色 {--color:rgb(var(--r),var(--g),var(--b));}.绿色 {--g:255;}.红色的 {--r:255;}p{颜色:var(--color);}h1{边框底部:1px 实心 var(--color);}

<h1 >红色标题</h1><p >是的,我是红色的:D</p>

<div class="绿色"><h1>绿色标题</h1><p >是的,我是绿色的 :D</p>

I'm attempting to scale size via a var custom property in a way that the classes would compose without being coupled. The desired effect is that the 3 lists would be at 3 different scales but as demonstrated on CodePen all 3 lists are the same scale. I'm looking for an explanation of the scoping and a CSS custom property technique that could achieve this with composable loosely coupled code.

:root {
  --size-1: calc(1 * var(--scale, 1) * 1rem);
  --size-2: calc(2 * var(--scale, 1) * 1rem);
  --size-3: calc(3 * var(--scale, 1) * 1rem);
}

.size-1 { font-size: var(--size-1) }
.size-2 { font-size: var(--size-2) }
.size-3 { font-size: var(--size-3) }

.scale-1x { --scale: 1 }
.scale-2x { --scale: 2 }
.scale-3x { --scale: 3 }

html {
  font: 1em sans-serif;
  background: papayawhip;
}

ol {
  float: left;
  list-style: none;
  margin: 1rem;
}

<ol class="scale-1x">
  <li class="size-1">size 1</li>
  <li class="size-2">size 2</li>
  <li class="size-3">size 3</li>
</ol>
<ol class="scale-2x">
  <li class="size-1">size 1</li>
  <li class="size-2">size 2</li>
  <li class="size-3">size 3</li>
</ol>
<ol class="scale-3x">
  <li class="size-1">size 1</li>
  <li class="size-2">size 2</li>
  <li class="size-3">size 3</li>
</ol>

解决方案

In your case you have evaluated the --scale custom property at the root level to define the --size-* properties and then you defined the --scale again inside child elements. This will not trigger evaluation again because it was already done in an upper level.

Here is a simple example to illustrate the issue:

.box {
  --color: var(--c, blue);
}

span {
  color: var(--color);
}

<div>
  <div class="box"><!-- --c is evaluated at this level -->
    <span style="--c:red">I will not be red because the property is already evaluated and --color is set to blue using the default value</span>
  </div>
</div>

<div style="--c:red">
  <div class="box"><!-- --c is evaluated at this level -->
    <span>I will be red because at the time of the evaluation --c is red (inherited from the upper div)</span>
  </div>
</div>

To fix your issue, you need to move the declaration from :root to the same level as the --scale definition:

.scale {
  --size-1: calc(1 * var(--scale, 1) * 1rem);
  --size-2: calc(2 * var(--scale, 1) * 1rem);
  --size-3: calc(3 * var(--scale, 1) * 1rem);
}

.size-1 { font-size: var(--size-1) }
.size-2 { font-size: var(--size-2) }
.size-3 { font-size: var(--size-3) }

.scale-1x { --scale: 1 }
.scale-2x { --scale: 2 }
.scale-3x { --scale: 3 }


html {
  font: 1em sans-serif;
  background: papayawhip;
}

ol {
  float: left;
  list-style: none;
  margin: 1rem;
}

<ol class="scale-1x scale">
  <li class="size-1">size 1</li>
  <li class="size-2">size 2</li>
  <li class="size-3">size 3</li>
</ol>
<ol class="scale-2x scale">
  <li class="size-1">size 1</li>
  <li class="size-2">size 2</li>
  <li class="size-3">size 3</li>
</ol>
<ol class="scale-3x scale">
  <li class="size-1">size 1</li>
  <li class="size-2">size 2</li>
  <li class="size-3">size 3</li>
</ol>

In this case, --scale is defined at the same level as its evaluation so --size-* will be defined correctly for each case.


From the specification:

To substitute a var() in a property’s value:

  1. If the custom property named by the first argument to the var() function is animation-tainted, and the var() function is being used in the animation property or one of its longhands, treat the custom property as having its initial value for the rest of this algorithm.
  2. If the value of the custom property named by the first argument to the var() function is anything but the initial value, replace the var() function by the value of the corresponding custom property. Otherwise,
  3. if the var() function has a fallback value as its second argument, replace the var() function by the fallback value. If there are any var() references in the fallback, substitute them as well.
  4. Otherwise, the property containing the var() function is invalid at computed-value time

In your first situation, you are falling into 3 because there is no value specified for --scale at the root level. In the last case we are falling into 2 because we defined --scale at the same level and we have its value.


In all the cases, we should avoid any evaluation at :root level because it's simply useless. The root level is the uppermost level in the DOM so all the elements will inherit the same value and it's impossible to have different values inside the DOM unless we evaluate the variable again.

Your code is equivalent to this one:

:root {
  --size-1: calc(1 * 1 * 1rem);
  --size-2: calc(2 * 1 * 1rem);
  --size-3: calc(3 * 1 * 1rem);
}


Let's take another example:

:root {
  --r:0;
  --g:0;
  --b:255;
  --color:rgb(var(--r),var(--g),var(--b))
}
div {
  color:var(--color);
}
p {
  --g:100;
  color:var(--color);
}

<div>
  some text
</div>
<p>
  some text
</p>

Intuitively, we may think that we can change the --color by changing one of the 3 variables defined at :root level but we cannot do this and the above code is the same as this one:

:root {
  --color:rgb(0,0,255)
}
div {
  color:var(--color);
}
p {
  --g:100;
  color:var(--color);
}

<div>
  some text
</div>
<p>
  some text
</p>

The 3 variables (--r, --g, --b) are evaluated inside :root so we have already substituted them with their values.

In such a situation we have 3 possibilities:

:root {
  --r:0;
  --g:0;
  --b:255;
  --color:rgb(var(--r),var(--g),var(--b))
}
div {
  color:var(--color);
}
p {
  --g:200; /*this will not have any effect !*/
  color:var(--color);
}

:root {
  --g:200; /*this will work*/
}

<div>
  some text
</div>
<p>
  some text
</p>

:root {
  --r:0;
  --g:0;
  --b:255;
  --color:rgb(var(--r),var(--g),var(--b))
}
div {
  color:var(--color);
}
p {
  --g:200;
  --color:rgb(var(--r),var(--g),var(--b));
  color:var(--color);
}

<div>
  some text
</div>
<p>
  some text
</p>

* {
  --r:0;
  --g:0;
  --b:255;
  --color:rgb(var(--r),var(--g),var(--b))
}
div {
  color:var(--color);
}
p {
  --g:200;
  color:var(--color);
}

<div>
  some text
</div>
<p>
  some text
</p>


Considering this, we should always keep the evaluation at the lowest possible point in the DOM tree and especially after the variable changes (or at the same level)

Here is what we shouldn't do

:root {
  --r: 0;
  --g: 0;
  --b: 0;
}
.color {
  --color: rgb(var(--r), var(--g), var(--b))
}
.green {
  --g: 255;
}
.red {
  --r: 255;
}
p {
  color: var(--color);
}

h1 {
  border-bottom: 1px solid var(--color);
}

<div class="color">
  <h1 class="red">Red </h1>
  <p class="red">I want to be red :(</p>
</div>
<div class="color">
  <h1 class="green">Green </h1>
  <p class="green">I want to be green :(</p>
</div>

Here is what we should do

:root {
  --r:0;
  --g:0;
  --b:0;
}
.color {
  --color:rgb(var(--r),var(--g),var(--b));
}

.green {
  --g:255;
}

.red {
  --r:255;
}

p {
  color:var(--color);
}
h1 {
  border-bottom: 1px solid var(--color);
}

<div class="red">
  <h1 class="color">Red title</h1>
  <p class="color">Yes I am red :D</p>
</div>
<div class="green">
  <h1 class="color">Green title</h1>
  <p class="color">Yes I am green :D</p>
</div>

We can also do like this:

:root {
  --r:0;
  --g:0;
  --b:0;
}
.color {
  --color:rgb(var(--r),var(--g),var(--b));
}

.green {
  --g:255;
}

.red {
  --r:255;
}

p {
  color:var(--color);
}
h1 {
  border-bottom: 1px solid var(--color);
}

<div class="red color">
  <h1 >Red title</h1>
  <p >Yes I am red :D</p>
</div>
<div class="green color">
  <h1>Green title</h1>
  <p >Yes I am green :D</p>
</div>

这篇关于用于计算外部作用域中的变量时忽略 CSS 作用域自定义属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆