wandres.dev
RENDIMIENTO DEL CSS · contain, content-visibility y el coste

contain: las cuatro garantías y qué compra cada una

size, layout, style y paint no son niveles de optimización sino promesas concretas que le haces al motor. Qué promete cada una, qué te quita a cambio y cuál usar de verdad.

⏱ 18 min

contain es la propiedad peor explicada del CSS moderno porque casi siempre se presenta como un interruptor de rendimiento, y no lo es. Es un contrato: le prometes al motor que cierta información no cruza la frontera de este elemento, y a cambio el motor puede saltarse trabajo que de otro modo tendría que hacer por si acaso. Cada valor es una promesa distinta, con un precio distinto, y usar el equivocado no es “menos óptimo”: es romper el layout.

🎯 Al terminar esta lección sabrás
  • Enunciar la garantía exacta que da cada uno de los cuatro tipos.
  • Explicar qué trabajo se ahorra el motor con cada garantía.
  • Reconocer el efecto secundario de layout y paint sobre el posicionamiento.
  • Elegir el valor correcto para los tres casos que aparecen en la práctica.

Las cuatro garantías

layout: la geometría de dentro no afecta a la de fuera

La promesa: el layout interno del elemento es independiente del layout externo, y viceversa. Nada de lo que ocurra dentro puede cambiar la posición o el tamaño de nada de fuera, salvo a través del tamaño del propio elemento.

Lo que el motor se ahorra: puede calcular el layout del subárbol de forma aislada, y —más importante— cuando algo cambia dentro, la invalidación no se propaga hacia los lados. Sin containment, un cambio en un elemento puede obligar a recalcular a sus hermanos y a sus tíos, porque el motor no puede descartar que exista una interacción. Con contain: layout, sabe que no la hay.

Lo que te cuesta: el elemento pasa a establecer un contexto de formato independiente, y eso arrastra tres efectos que hay que conocer porque son cambios de comportamiento, no de rendimiento:

  • Se convierte en bloque contenedor de sus descendientes posicionados de forma absoluta o fija. Un hijo con position: fixed dentro deja de anclarse a la ventana y pasa a anclarse a él.
  • Crea un contexto de apilamiento, así que el z-index de sus descendientes queda encerrado dentro.
  • Los flotantes y los márgenes de dentro no interactúan con los de fuera.

Ese primer efecto es el que rompe cosas de verdad. Una cabecera fija dentro de un contenedor con contain: layout deja de ser fija respecto a la pantalla. Es el mismo mecanismo que hace que transform rompa a position: fixed, y produce el mismo desconcierto.

paint: nada se dibuja fuera de los límites

La promesa: ningún descendiente pinta fuera de la caja de relleno del elemento. Lo que sobresale, se recorta.

Lo que el motor se ahorra: si el elemento está fuera de la pantalla, el motor puede garantizar que todos sus descendientes también lo están y saltarse su pintado por completo. Sin esa garantía tendría que considerar que un hijo con position: absolute y coordenadas negativas pudiera estar visible.

Lo que te cuesta: el recorte es real. Un menú desplegable, un tooltip o una sombra que sobresalgan del contenedor desaparecen. Además, paint también crea contexto de apilamiento y bloque contenedor para posicionados, igual que layout.

size: el tamaño no depende del contenido

La promesa: el tamaño del elemento se puede calcular sin mirar a sus hijos. El motor lo dimensiona como si estuviera vacío.

Lo que el motor se ahorra: puede calcular la geometría de todo lo que rodea al elemento sin haber calculado su contenido. Y cuando el contenido cambia, la invalidación no sube: el padre no necesita recalcularse porque su hijo no puede cambiar de tamaño por dentro.

Lo que te cuesta: muchísimo, y por eso es la que casi nunca se usa sola. Si el elemento no tiene un tamaño especificado, colapsa a cero. Un div con contain: size y sin altura declarada desaparece aunque tenga contenido.

Por eso existe inline-size, que contiene solo la dimensión en línea. El ancho se calcula sin mirar el contenido, pero la altura sigue creciendo con él. Es el modo seguro de la contención de tamaño, y es exactamente lo que activa container-type: inline-size para las container queries: un contenedor tiene que poder resolver su ancho sin consultar a sus hijos, porque si no habría una dependencia circular —los hijos consultan el ancho del contenedor que depende de los hijos—.

style: los contadores no escapan

La promesa: las propiedades que pueden tener efectos fuera del elemento y sus descendientes no los tienen. En la práctica esto significa los contadores CSS y las comillas.

Lo que el motor se ahorra: poco, comparado con las otras. Sin contención de estilo, incrementar un contador dentro de un subárbol puede afectar a la numeración de elementos posteriores en cualquier parte del documento, así que el motor debe reconsiderarlos.

Lo que te cuesta: los contadores de dentro dejan de participar en la numeración global. Si estabas numerando secciones a lo largo del documento, cada elemento contenido empieza de cero.

Es la garantía menos útil de las cuatro y la que menos se usa por separado. Va incluida en los atajos.

Los atajos y qué elegir

Valor Equivale a
contain: content layout style paint
contain: strict size layout style paint

content es el que se usa el noventa por ciento de las veces: da las garantías útiles sin el riesgo de que el elemento colapse. strict solo tiene sentido cuando el elemento tiene un tamaño fijo garantizado.

En la práctica, los tres casos que aparecen de verdad:

Elementos repetidos e independientes en una lista larga. Filas de una tabla, tarjetas de un catálogo, mensajes de un chat. Cada uno es autónomo y no interactúa con sus hermanos.

.fila {
  contain: content;
}

Un contenedor cuyo contenido cambia con frecuencia. Un panel de registro, un widget que se actualiza cada segundo, una vista con datos en vivo. Aquí la ganancia es que la invalidación no sale del panel.

.panel-vivo {
  contain: layout paint;
}

Un contenedor de tamaño conocido con contenido pesado. Aquí sí compensa strict, siempre con el tamaño declarado:

.miniatura {
  contain: strict;
  inline-size: 240px;
  block-size: 160px;
}
⚠️
No lo pongas en todas partes

contain no es gratis: cada elemento contenido es una barrera más que el motor gestiona, y establecer contextos de formato y de apilamiento tiene un coste propio. Aplicarlo con un selector universal es una forma excelente de empeorar el rendimiento y romper el posicionamiento a la vez. Se aplica a los pocos elementos donde has identificado que la invalidación se propaga, y se comprueba midiendo.

Los efectos secundarios son la parte importante

Si de esta lección solo retienes una cosa, que sea que contain: layout y contain: paint cambian el comportamiento del layout, no solo su velocidad. La lista, para tenerla a mano:

Valor Bloque contenedor de posicionados Contexto de apilamiento Contexto de formato independiente
layout
paint
size no no no
style no no no

Los dos primeros son, a efectos de posicionamiento, equivalentes a haber puesto un transform en el elemento. Si añades contain: content a las tarjetas de un catálogo y de repente un menú desplegable se recorta o un elemento fijo deja de serlo, no es un bug del navegador: es la garantía que has firmado.

Containment no acelera nada: elimina la incertidumbre, y la incertidumbre es lo caro

La idea que hay que interiorizar, y que se aplica muchísimo más allá de esta propiedad, es que un motor de renderizado gasta la mayor parte de su esfuerzo no en hacer trabajo, sino en no poder descartarlo. Cuando cambia un elemento, el motor no sabe si eso puede afectar al tamaño de su abuelo, a la posición de sus hermanos o a lo que se ve en la esquina opuesta de la pantalla; y como no lo sabe, tiene que asumir que sí. Todo el diseño de CSS conspira contra la certeza: la herencia, la altura automática que depende del contenido, el colapso de márgenes, los flotantes, los posicionados que pueden anclarse a cualquier antepasado, los contadores que atraviesan el documento. Cada una de esas funciones es útil y cada una impide una optimización. contain es la primera propiedad del lenguaje cuyo propósito no es producir un efecto visual sino devolverle al motor información que el modelo le había quitado, y por eso su forma es la de un contrato y no la de un ajuste: las garantías cuestan exactamente lo que valen. Si prometes que el tamaño no depende del contenido, ese elemento ya no puede crecer con su contenido, y no hay ninguna forma de tener las dos cosas. Esa simetría es la clave para usar bien la propiedad y también para reconocer a quien no la ha entendido: si alguien te dice que añadir contain a todo mejora el rendimiento sin contrapartidas, lo que está diciendo es que ha firmado un montón de contratos sin leerlos, y el precio va a aparecer más tarde con la forma de un tooltip recortado que nadie sabe explicar.