Propiedad personalizada con alcance CSS ignorada cuando se usa para calcular la variable en el alcance externo
Estoy intentando escalar el tamaño a través de una var
propiedad personalizada de manera que las clases se compongan sin estar acopladas. El efecto deseado es que las 3 listas estén en 3 escalas diferentes pero, como se demuestra en CodePen, las 3 listas tienen la misma escala. Estoy buscando una explicación del alcance y una técnica de propiedad personalizada de CSS que pueda lograr esto con código componible débilmente acoplado.
: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>
En su caso, evaluó la --scale
propiedad personalizada en el nivel raíz para definir las --size-*
propiedades y luego definió --scale
nuevamente los elementos secundarios internos. Esto no activará nuevamente la evaluación porque ya se hizo en un nivel superior .
He aquí un ejemplo sencillo para ilustrar el problema:
.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>
Para solucionar su problema, debe mover la declaración :root
al mismo nivel que la --scale
definición:
Mostrar fragmento de código
En este caso, --scale
se define al mismo nivel que su evaluación por lo que --size-*
se definirá correctamente para cada caso.
De la especificación :
Para sustituir una var() en el valor de una propiedad:
- Si la propiedad personalizada nombrada por el primer argumento de la función var() está contaminada por la animación y la función var() se está utilizando en la propiedad de animación o en una de sus manos, trate la propiedad personalizada como si tuviera su valor inicial para la resto de este algoritmo.
- Si el valor de la propiedad personalizada nombrada por el primer argumento de la función var() no es el valor inicial, reemplace la función var() por el valor de la propiedad personalizada correspondiente. De lo contrario,
- Si la función var() tiene un valor de reserva como segundo argumento, reemplace la función var() por el valor de reserva. Si hay referencias var() en el respaldo, sustitúyalas también.
- De lo contrario, la propiedad que contiene la función var() no es válida en el momento del valor calculado
En su primera situación, está cayendo en 3 porque no hay ningún valor especificado --scale
en el nivel raíz. En el último caso estamos cayendo en 2 porque lo definimos --scale
en el mismo nivel y tenemos su valor.
En todos los casos debemos evitar cualquier evaluación a :root
nivel porque es simplemente inútil. El nivel raíz es el nivel más alto en el DOM, por lo que todos los elementos heredarán el mismo valor y es imposible tener valores diferentes dentro del DOM a menos que evalúemos la variable nuevamente.
Tu código es equivalente a este:
:root {
--size-1: calc(1 * 1 * 1rem);
--size-2: calc(2 * 1 * 1rem);
--size-3: calc(3 * 1 * 1rem);
}
Tomemos otro ejemplo:
: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>
Intuitivamente podemos pensar que podemos cambiar --color
cambiando una de las 3 variables definidas en :root
el nivel pero no podemos hacer esto y el código anterior es el mismo que este:
:root {
--color:rgb(0,0,255)
}
div {
color:var(--color);
}
p {
--g:100;
color:var(--color);
}
<div>
some text
</div>
<p>
some text
</p>
Las 3 variables ( --r
, --g
, --b
) están evaluadas dentro :root
por lo que ya las hemos sustituido con sus valores.
En tal situación tenemos 3 posibilidades:
- Cambie las variables dentro del
:root
uso de JS u otra regla CSS. Esto no nos permitirá tener diferentes colores:
Mostrar fragmento de código
- Evalúe la variable nuevamente dentro del elemento necesario. En este caso, perderemos cualquier tipo de flexibilidad y la definición interna
:root
se volverá inútil (o al menos se convertirá en el valor predeterminado):
Mostrar fragmento de código
- Cambiar el
:root
selector con el selector universal*
. Esto asegurará que nuestra función esté definida y evaluada en todos los niveles. En algún caso complejo, esto puede tener algunos resultados no deseados.
Mostrar fragmento de código
Considerando esto, siempre debemos mantener la evaluación en el punto más bajo posible en el árbol DOM y especialmente después de que la variable cambie (o en el mismo nivel)
Esto es lo que no deberíamos hacer
Mostrar fragmento de código
Esto es lo que debemos hacer
Mostrar fragmento de código
También podemos hacer así:
Mostrar fragmento de código