Propiedad personalizada con alcance CSS ignorada cuando se usa para calcular la variable en el alcance externo

Resuelto ryanve asked hace 6 años • 0 respuestas

Estoy intentando escalar el tamaño a través de una varpropiedad 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>
Expandir fragmento

ryanve avatar Aug 25 '18 16:08 ryanve
Aceptado

En su caso, evaluó la --scalepropiedad 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>
Expandir fragmento

Para solucionar su problema, debe mover la declaración :rootal mismo nivel que la --scaledefinición:

Mostrar fragmento de código

En este caso, --scalese 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:

  1. 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.
  2. 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,
  3. 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.
  4. 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 --scaleen el nivel raíz. En el último caso estamos cayendo en 2 porque lo definimos --scaleen el mismo nivel y tenemos su valor.


En todos los casos debemos evitar cualquier evaluación a :rootnivel 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>
Expandir fragmento

Intuitivamente podemos pensar que podemos cambiar --colorcambiando una de las 3 variables definidas en :rootel 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>
Expandir fragmento

Las 3 variables ( --r, --g, --b) están evaluadas dentro :rootpor lo que ya las hemos sustituido con sus valores.

En tal situación tenemos 3 posibilidades:

  • Cambie las variables dentro del :rootuso 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 :rootse volverá inútil (o al menos se convertirá en el valor predeterminado):

Mostrar fragmento de código

  • Cambiar el :rootselector 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

Temani Afif avatar Aug 25 '2018 09:08 Temani Afif