La cadena de valores: de declarado a actual
Los seis estados por los que pasa el valor de una propiedad, qué ocurre en cada transición, cuál es el que se hereda y por qué getComputedStyle no devuelve lo que su nombre promete.
Un valor de CSS no es una cosa: es una secuencia de seis cosas, cada una calculada a partir de la anterior en un momento distinto del pipeline. La mayoría de las preguntas incómodas de este nivel —por qué los em se acumulan, por qué no se puede animar hacia auto, por qué una variable con un valor inválido no cae a la declaración anterior— se responden señalando en qué eslabón de la cadena ocurre cada cosa.
- Nombrar los seis estados del valor de una propiedad y qué los distingue.
- Identificar en qué transición actúan la cascada, la herencia y el layout.
- Explicar por qué el valor que se hereda es el calculado y no otro.
- Interpretar correctamente lo que devuelve
getComputedStyle.
Seis estados
flowchart TB d[Valor declarado] --> c[Valor cascadeado] c --> e[Valor especificado] e --> comp[Valor calculado] comp --> u[Valor usado] u --> a[Valor actual] casc[La cascada ordena y elige uno] --> c defec[Defecto por herencia o por valor inicial] --> e maq[El layout resuelve auto y porcentajes] --> u disp[Limites del dispositivo y de las fuentes] --> a comp --> hijo[Es lo que se hereda a los hijos] style d fill:#89b4fa,color:#11111b style c fill:#cba6f7,color:#11111b style e fill:#cba6f7,color:#11111b style comp fill:#a6e3a1,color:#11111b style u fill:#f9e2af,color:#11111b style a fill:#fab387,color:#11111b style hijo fill:#94e2d5,color:#11111b
Declarado. Cada declaración que sobrevive al filtrado —selector que casa, condiciones ciertas, sintaxis válida— aporta un valor declarado. Para una propiedad de un elemento puede haber muchos, o ninguno.
Cascadeado. El ganador de la ordenación de los seis criterios. Puede no existir: si ninguna declaración aplicaba, no hay valor cascadeado, y eso no es un error.
Especificado. El resultado de aplicar el mecanismo de defecto. Si había valor cascadeado, normalmente es ese mismo; si no lo había, se rellena heredando o con el valor inicial; y si el valor cascadeado era una de las palabras clave globales, se sustituye por lo que esa palabra indique. Siempre existe un valor especificado para toda propiedad de todo elemento.
Calculado. El especificado, resuelto todo lo que se puede resolver sin hacer layout. Aquí las unidades relativas se absolutizan, ciertos porcentajes se multiplican por su referencia, palabras clave como bolder se sustituyen por su equivalente numérico, y las direcciones relativas se convierten en absolutas.
Usado. El calculado, terminado de resolver con el layout ya hecho. Es donde auto se convierte en un número de píxeles y donde se resuelven los porcentajes que dependían de dimensiones que no se conocían antes.
Actual. El usado, ajustado a lo que el dispositivo puede realmente hacer: redondeos a píxeles enteros donde el motor no puede subdividir, sustituciones de fuente cuando la pedida no está disponible.
Qué ocurre en cada transición
| Transición | Quién actúa | Ejemplo |
|---|---|---|
| Declarado a cascadeado | La cascada, con sus seis criterios | Tres reglas para color, gana una |
| Cascadeado a especificado | Herencia, valor inicial y palabras globales | Sin declaración, color hereda del padre |
| Especificado a calculado | Absolutización sin layout | font-size: 1.5em pasa a 24px |
| Calculado a usado | El layout | inline-size: auto pasa a 640px |
| Usado a actual | El dispositivo | border-width: 0.7px pasa a 1px |
Dos transiciones merecen detalle porque explican comportamientos que se dan de bruces con la intuición.
La de especificado a calculado es donde se absolutiza lo relativo, y por tanto donde se decide qué se transmite hacia abajo. Un font-size: 1.5em deja de ser una proporción y pasa a ser una medida concreta antes de que ningún hijo lo vea. Ese es el mecanismo del efecto acumulativo de los em anidados.
La de calculado a usado es la que hace imposible cierta clase de peticiones. Como el valor calculado de inline-size: auto sigue siendo auto, y las transiciones interpolan entre valores calculados, no hay dos números entre los que interpolar y por tanto no hay animación posible hacia auto. No es una limitación de implementación: es una consecuencia de en qué eslabón vive cada cosa.
Lo que se hereda es el valor calculado
La especificación es explícita: el valor heredado de una propiedad en un elemento es el valor calculado de esa propiedad en su padre. Ni el declarado, ni el usado.
Que sea el calculado y no el declarado es lo que produce la acumulación de las unidades relativas, y también lo que hace que un line-height en porcentaje se herede ya convertido en una longitud fija mientras que uno sin unidad se hereda como número y cada descendiente lo recalcula.
Que sea el calculado y no el usado tiene una consecuencia igual de importante y menos conocida: los porcentajes que dependen del layout no se heredan resueltos. Un inline-size: 50% en un padre no transmite a los hijos “la mitad del contenedor del padre en píxeles”; simplemente no se transmite, porque inline-size no es heredable. Pero para las propiedades que sí lo son y admiten porcentaje, lo que baja es lo que el motor pudo resolver sin mirar la geometría.
Y hay una tercera consecuencia que ata este nivel con el anterior: como la herencia toma valores calculados, y revert, unset e inherit actúan en la transición de cascadeado a especificado, todo el mecanismo de defecto ocurre antes de que exista el valor calculado. Por eso ninguna de esas palabras clave puede depender del layout, y por eso funcionan igual en cualquier contexto.
Esta es la trampa que convierte la teoría de la cadena en algo que muerde en producción. La función getComputedStyle no devuelve el valor calculado de la especificación de CSS, sino lo que el modelo de objetos llama el valor resuelto, que es una mezcla definida propiedad a propiedad: para unas devuelve el valor calculado y para otras, cuando el elemento tiene caja, devuelve el valor usado. Las propiedades geométricas —anchos, altos, márgenes, rellenos, desplazamientos de los posicionados— están en el segundo grupo, así que pedir inline-size de un elemento con auto te devuelve una cifra en píxeles y no la palabra auto. Eso a veces es justo lo que quieres, y por eso el comportamiento se estandarizó, pero produce dos sorpresas caras. La primera es de exactitud: si lees una propiedad esperando el valor calculado y recibes el usado, cualquier cálculo que hagas encima cambia de significado según el estado del layout, y el mismo código devuelve cosas distintas si el elemento está oculto, porque un elemento sin caja no tiene valor usado. La segunda es de rendimiento, y es peor: como el valor usado exige que el layout esté al día, leerlo obliga al navegador a ejecutar el layout de forma síncrona si hay cambios pendientes. Un bucle que escribe estilos y lee medidas alternando provoca un layout completo por vuelta, y ese patrón no se ve en el código porque son dos líneas normales. La disciplina que lo evita es siempre la misma y conviene automatizarla mentalmente: agrupa todas las lecturas antes de todas las escrituras. Y si lo que necesitas es geometría, pídela con las APIs de geometría en lugar de con los estilos, que al menos deja claro en el código que estás midiendo y no consultando.
Dónde encaja cada cosa que ya sabes
La cadena ordena de golpe varias piezas de los niveles anteriores.
La cascada vive entre declarado y cascadeado. Todo lo que aprendiste sobre orígenes, capas y especificidad ocurre ahí y solo ahí.
La herencia y las palabras clave globales viven entre cascadeado y especificado. Por eso un valor heredado nunca compite con una declaración: llega después de que la competición haya terminado.
La sustitución de custom properties ocurre en el paso a calculado, y por eso un valor inválido dentro de una variable no puede caer a la declaración anterior: cuando se descubre el problema, la cascada ya eligió hace rato y las alternativas se descartaron. El comportamiento definido para ese caso es tratar la propiedad como unset, que es exactamente lo que la cadena permite hacer en ese punto.
El layout vive entre calculado y usado, y todo lo que aprenderás sobre modelos de caja y algoritmos de dimensionado ocurre ahí.
Y la composición en pantalla vive después del valor actual. Una propiedad que no llega a tener valor usado —porque no aplica a ese tipo de caja, o porque el elemento no genera caja— nunca llega a pintarse, por muy bien que haya ganado la cascada.