wandres.dev
ONTOLOGÍA · El mapa del CSS moderno

Las etapas del motor: dónde ocurre cada cosa que escribes

El recorrido desde una hoja de estilos hasta un píxel encendido, en cuatro etapas irreversibles, y qué familia de propiedades interviene en cada una.

⏱ 16 min

Toda propiedad de CSS entra en juego en una etapa concreta del motor, y las etapas van en un orden fijo que no admite marcha atrás. Saber en cuál interviene cada familia de propiedades responde de golpe a preguntas que de otro modo parecen esotéricas: por qué un elemento transformado sigue ocupando su hueco original, por qué opacity es barata y width cara, o por qué ninguna propiedad de pintura puede conseguir que un texto quepa. Esta lección no optimiza nada todavía: solo coloca el mapa de etapas encima del mapa de regiones.

🎯 Al terminar esta lección sabrás
  • Enumerar las cuatro etapas del pipeline de renderizado y qué produce cada una.
  • Asignar una propiedad cualquiera a la etapa en la que interviene.
  • Explicar por qué el orden de las etapas hace imposibles ciertos efectos.
  • Reconocer las tres estructuras de datos intermedias y para qué sirven.

Cuatro etapas y tres estructuras intermedias

El navegador no pasa de tu CSS a los píxeles de un salto. Construye estructuras intermedias, y cada una responde a una pregunta distinta.

Primero hay un árbol de elementos, que viene del HTML, y un modelo de las hojas de estilo. Con ambos, el motor ejecuta el recálculo de estilo: para cada elemento y cada propiedad, resuelve qué valor gana. El resultado es el conjunto de valores calculados, y es la frontera entre el mundo de los selectores y el mundo de la geometría. A partir de aquí los selectores ya no existen: solo hay elementos con propiedades resueltas.

Con esos valores el motor construye el árbol de cajas, que no es igual al árbol de elementos —un elemento con display: none no genera caja, uno con display: contents genera hijos pero no caja propia, y el motor inserta cajas anónimas donde el modelo lo exige— y ejecuta el layout: calcular tamaño y posición de cada caja. Es la etapa más cara, porque el tamaño de una caja depende de sus hijos y su posición depende de sus hermanos, y esas dependencias se propagan.

Después viene la pintura, que decide qué se dibuja y en qué orden: fondos, bordes, texto, sombras, imágenes. Aquí es donde importan los contextos de apilamiento, porque el orden de pintado es lo que produce la sensación de profundidad.

Y por último la composición, que toma los resultados ya pintados —posiblemente en capas independientes— y los combina para presentarlos en pantalla, típicamente con ayuda de la GPU.

flowchart TB
hojas[Hojas de estilo] --> modelo[Modelo de las reglas]
doc[Documento HTML] --> arbol[Arbol de elementos]
modelo --> recalc[Etapa 1 recalculo de estilo]
arbol --> recalc
recalc -->|valores calculados| cajas[Arbol de cajas]
cajas --> maqueta[Etapa 2 layout]
maqueta -->|tamano y posicion| pinta[Etapa 3 pintura]
pinta -->|ordenes de dibujo| compone[Etapa 4 composicion]
compone --> pantalla[Pixeles en pantalla]
style recalc fill:#89b4fa,color:#11111b
style maqueta fill:#f9e2af,color:#11111b
style pinta fill:#cba6f7,color:#11111b
style compone fill:#fab387,color:#11111b
style pantalla fill:#a6e3a1,color:#11111b

Qué propiedad vive en qué etapa

La clasificación práctica es esta, y merece la pena memorizarla porque explica el coste y las limitaciones de casi todo.

Etapa Familia de propiedades Ejemplos
Estilo Todas, pero solo se resuelven aquí las que no generan caja color, --token, font-size
Layout Las que afectan a tamaño o posición width, padding, display, grid-template-columns, position
Pintura Las que afectan al aspecto sin mover cajas background, box-shadow, border-radius, color
Composición Las que se pueden aplicar a una superficie ya pintada transform, opacity, filter

De la tabla salen consecuencias que se suelen aprender por accidente. Cambiar width obliga a rehacer layout, pintura y composición, porque cada etapa depende de la anterior. Cambiar background-color no obliga a rehacer el layout, porque el tamaño de la caja no depende del color. Y cambiar transform no obliga a rehacer ni layout ni pintura si el elemento ya está en su propia capa de composición: por eso animar posición con transform es incomparablemente más barato que animarla con left.

También sale la explicación de un comportamiento que desconcierta a todo el mundo la primera vez. Un elemento con transform: translateX(200px) se ve desplazado pero sus hermanos no se mueven, y su hueco original sigue reservado. No es un capricho: la transformación se aplica en una etapa posterior al layout, y el layout ya había terminado de decidir dónde va cada caja. La etapa 4 no puede pedirle a la etapa 2 que rehaga su trabajo.

El pipeline se puede recorrer hacia atrás, y ahí está la trampa de rendimiento

La regla de que las etapas van hacia adelante vale para CSS, pero JavaScript sí puede forzar el camino inverso, y ese es el origen del problema de rendimiento más caro y más invisible del frontend. Cuando lees una propiedad geométrica —offsetHeight, getBoundingClientRect(), scrollTop, o getComputedStyle() de algo que dependa del layout— el navegador está obligado a devolverte un valor correcto, y si tienes cambios de estilo pendientes no le queda más remedio que ejecutar el recálculo de estilo y el layout en ese mismo instante, de forma síncrona, antes de devolverte el número. Eso se llama layout forzado. Un bucle que en cada iteración escribe un estilo y después lee una medida provoca un layout completo por vuelta: con doscientos elementos son doscientos layouts en un fotograma que solo tiene dieciséis milisegundos. Lo peor es que el código no parece lento —son dos líneas inocentes— y el perfil solo muestra un pico difuso en el que nadie sospecha de una lectura. La cura es trivial una vez lo sabes: agrupa todas las lecturas primero y todas las escrituras después. Y el diagnóstico también: en el panel de rendimiento del navegador ese patrón aparece como una alternancia de barras de layout dentro de una sola tarea, y muchas herramientas lo etiquetan literalmente como reflow forzado.

Lo que el orden de etapas hace imposible

Hay una clase entera de peticiones que no se pueden satisfacer, y reconocerlas ahorra horas de búsqueda inútil.

No puedes hacer que la pintura influya en el layout. Ninguna sombra, ningún filtro y ninguna transformación cambia el espacio que ocupa un elemento. Si necesitas que el espacio cambie, tienes que tocar una propiedad de layout.

No puedes consultar el resultado del layout desde CSS y usarlo como entrada del propio layout. Esta es la razón profunda de que las container queries tengan la regla del containment: para preguntar por el tamaño de un contenedor sin caer en una dependencia circular, el motor necesita la garantía de que el contenido interior no puede alterar la dimensión que estás consultando. Sin esa garantía el sistema no converge, y la especificación prefiere prohibirlo a producir resultados inestables.

No puedes esperar que una etapa posterior arregle lo que una anterior decidió mal. Un texto que desborda su caja no se arregla con overflow, se oculta con overflow. El desbordamiento es información: te dice que el layout resolvió un tamaño insuficiente. Taparlo con una propiedad de pintura es tratar el síntoma.

Estas tres imposibilidades son las mismas en los cuatro motores, porque no derivan de una implementación sino de la estructura del modelo.

Cómo usar esto durante el resto de la guía

Cada vez que aparezca una propiedad nueva, colócala mentalmente en su etapa. Sirve para tres cosas concretas.

Sirve para predecir el coste sin medir nada: cuanto más arriba en el pipeline actúa, más caro es cambiarla en caliente.

Sirve para saber qué se puede animar sin sufrimiento: transform, opacity y filter viven en composición y no arrastran las etapas anteriores; width, top y margin arrastran el pipeline entero. Esa es toda la teoría detrás de la recomendación —repetida sin explicación en mil sitios— de animar con transformaciones.

Y sirve para acotar la depuración: si el problema es de tamaño o de posición, mirar propiedades de pintura es tiempo perdido; si el problema es de qué está encima de qué, mirar el layout es tiempo perdido. El mapa de regiones de la lección anterior te dice en qué zona buscar; el mapa de etapas te dice en qué momento del proceso se rompió.