用于在外部范围中计算var时,将忽略CSS范围的自定义属性

时间:2018-08-25 09:15:01

标签: css css3 scope css-variables

我正在尝试通过var自定义属性来缩放大小,以使类无需耦合即可组成。理想的效果是3个列表将以3个不同的比例缩放,但由于demo'd on 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 { 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>

1 个答案:

答案 0 :(得分:6)

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

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

.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>

要解决您的问题,您需要将声明从: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 { 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>

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


来自the specification

  

用属性值替换var()

     
      
  1. 如果自定义属性由var()的第一个参数命名   函数是动画污染的,并且var()函数正在   动画属性或其惯用手之一,请对待自定义   属性具有该算法其余部分的初始值。
  2.   
  3. 如果由第一个参数命名的自定义属性的值   var()函数除了初始值外什么都没有,请替换var()   通过相应的自定义属性的值来实现功能。否则,
  4.   
  5. 如果var()函数将后备值作为第二个参数,   用后备值替换var()函数。如果有的话   备用中的var()引用,也请替换它们。
  6.   
  7. 否则,包含var()函数的属性在   计算值时间
  8.   

在第一种情况下,您陷入了(3),因为在根级别没有为--scale指定值,但是在最后一种情况下,由于我们定义了{{1 }},并具有其价值。


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

您的代码与此等效:

:root

再举一个例子:

: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 {
  color:var(--color);
}
p {
  --g:100;
  color:var(--color);
}

直觉上,我们可能认为可以通过更改在<div> some text </div> <p> some text </p>级别定义的3个变量之一来更改--color,但不能。我们无法做到这一点,上面的代码与此代码相同:

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

<div> some text </div> <p> some text </p>内对3个变量(--r--g--b)求值,因此我们已经可以用它们的值替换它们。

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

  • 使用JS或其他CSS规则更改:root内部的变量,但不允许我们使用其他颜色:

:root
: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
: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);
}


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

这是我们不应该做的

<div>
  some text
</div>
<p>
  some text
</p>
: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>
: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>
: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);
}