wandres.dev
POSICIONAMIENTO · De static a fixed, y el bloque contenedor

El bloque contenedor: cómo se determina y contra qué mide

El árbol de decisión completo para saber cuál es el containing block de un elemento, qué caja concreta se usa en cada caso, y contra qué se resuelven los porcentajes.

⏱ 20 min

El bloque contenedor es el rectángulo contra el que se resuelven los porcentajes de tamaño, los márgenes, el padding y los desplazamientos de un elemento. Casi todo el mundo cree que es “la caja del padre”, y esa creencia es correcta menos veces de las que parece: depende del valor de position, unas veces se usa la caja de contenido y otras la de relleno, y hay una lista de propiedades que convierten a un ancestro cualquiera en bloque contenedor sin que nadie lo haya pedido.

🎯 Al terminar esta lección sabrás
  • Determinar el bloque contenedor de un elemento a partir de su position.
  • Distinguir cuándo se usa la caja de contenido y cuándo la de relleno.
  • Enumerar las propiedades que convierten a un ancestro en bloque contenedor de absolutos y fijos.
  • Predecir contra qué se resuelve cada porcentaje.

Las cuatro reglas

El procedimiento depende enteramente del valor de position del elemento en cuestión.

Regla 1. static, relative o sticky. El bloque contenedor es la caja de contenido del ancestro más cercano que sea un contenedor de bloque —un elemento con display: block, inline-block o list-item— o que establezca un contexto de formato: un contenedor flex, uno de rejilla, una tabla.

Ojo al detalle: es el ancestro más cercano que cumpla eso, no necesariamente el padre. Si el padre tiene display: inline y no establece contexto de formato, se salta y se busca más arriba.

Regla 2. absolute. El bloque contenedor es la caja de relleno del ancestro más cercano con position distinto de static. Si no hay ninguno, es el bloque contenedor inicial.

Que sea la caja de relleno y no la de contenido tiene una consecuencia que se ve en cuanto la buscas: un absoluto con inset: 0 se estira hasta cubrir el padding del ancestro, no solo su contenido.

Regla 3. fixed. El bloque contenedor es el viewport en medios continuos, o el área de página en medios paginados.

Regla 4, la que rompe las anteriores. Si el elemento es absolute o fixed, el bloque contenedor puede ser la caja de relleno del ancestro más cercano que tenga alguna de estas propiedades:

  • transform, translate, rotate, scale o perspective con valor distinto de none.
  • filter o backdrop-filter con valor distinto de none.
  • contain con valor layout, paint, strict o content. Por extensión, también content-visibility: auto, que aplica containment de layout y paint.
  • will-change nombrando cualquier propiedad de las anteriores.

Una propiedad que estaba en esta lista y ya no está: container-type. Hasta Chrome 129, Firefox 133 y Safari 18.4, declarar un contenedor de consultas aplicaba containment de layout y con él el bloque contenedor. El grupo de trabajo lo revirtió por considerarlo un error de diseño, y hoy container-type solo aplica containment de estilo y de tamaño y establece un contexto de formato independiente. Si tu layout dependía del comportamiento antiguo, se recupera declarando contain: layout de forma explícita, o position: relative si solo quieres el bloque contenedor sin el contexto de apilamiento.

Esta cuarta regla tiene prioridad sobre la tercera: un fixed dentro de un ancestro transformado deja de anclarse al viewport. Es el caso al que dedicamos la última lección del nivel.

flowchart TB
A[Cual es el bloque contenedor de este elemento] --> B{Que valor tiene position}
B -->|static relative o sticky| C[Caja de contenido del ancestro que es contenedor de bloque o crea contexto de formato]
B -->|absolute| D{Hay un ancestro con transform filter contain o will-change de esas}
B -->|fixed| E{Hay un ancestro con transform filter contain o will-change de esas}
D -->|Si| F[Caja de relleno de ese ancestro]
D -->|No| G{Hay un ancestro con position distinto de static}
G -->|Si| H[Caja de relleno de ese ancestro]
G -->|No| I[Bloque contenedor inicial]
E -->|Si| F
E -->|No| J[El viewport]
style A fill:#89b4fa,color:#11111b
style C fill:#a6e3a1,color:#11111b
style F fill:#f38ba8,color:#11111b
style H fill:#a6e3a1,color:#11111b
style I fill:#cba6f7,color:#11111b
style J fill:#a6e3a1,color:#11111b

El bloque contenedor inicial

Cuando un absoluto no encuentra ningún ancestro posicionado ni ninguno de la regla 4, cae en el bloque contenedor inicial: el rectángulo en el que vive el elemento raíz. Tiene las dimensiones del viewport y su origen está en el origen del documento.

No es lo mismo que el viewport, aunque midan igual. El bloque contenedor inicial está anclado al documento y se desplaza con el scroll; el viewport no. Por eso un absolute sin ancestro posicionado se queda donde lo pusiste y sube con la página, mientras que un fixed se queda pegado a la pantalla.

Tampoco es lo mismo que la caja de html o la de body, que son elementos normales con sus propios márgenes. Un absolute con inset-block-start: 0 sin ancestro posicionado se coloca en la mismísima esquina del documento, ignorando el margen de ocho píxeles que el estilo de usuario le da a body.

Contra qué se resuelven los porcentajes

Una vez sabes cuál es el bloque contenedor, la resolución de porcentajes es mecánica:

  • block-size, height, inset-block-start, inset-block-end, top y bottom se resuelven contra la altura del bloque contenedor.
  • inline-size, width, inset-inline-start, inset-inline-end, left y right se resuelven contra la anchura.
  • Todo el padding y todo el margin, incluidos los del eje de bloque, se resuelven contra la anchura del bloque contenedor. Sí, también margin-block-start y padding-block-end.

Esa última es la que sorprende y la que hace posible el truco clásico de la proporción constante: un padding-block-start: 56.25% produce una caja con proporción 16 a 9 porque el porcentaje mide contra la anchura. Hoy ese truco está obsoleto —aspect-ratio hace lo mismo y se lee— pero entender por qué funcionaba es entender la regla.

Hay una trampa muy concreta con los porcentajes de altura. block-size: 100% solo funciona si el bloque contenedor tiene una altura definida. Si el ancestro tiene altura automática, el porcentaje no puede resolverse y el valor se trata como auto. Es la razón por la que la cadena html, body { block-size: 100% } era obligatoria antes de que existieran las unidades de viewport y 100dvh.

/* falla: el padre no tiene altura definida */
.padre { }
.hijo  { block-size: 100%; }   /* se comporta como auto */

/* funciona: el padre define su altura */
.padre { block-size: 20rem; }
.hijo  { block-size: 100%; }

Los elementos absolutos son la excepción: para ellos, un block-size: 100% sí se resuelve, porque su bloque contenedor —la caja de relleno del ancestro posicionado— siempre tiene una altura ya determinada en el momento del cálculo.

Contexto de apilamiento y bloque contenedor son dos cosas distintas que casi siempre viajan juntas

Aquí está el matiz que separa entender de haber memorizado. Las listas de propiedades que crean contexto de apilamiento y las que crean bloque contenedor para absolutos y fijos se parecen mucho pero no son iguales, y confundirlas produce diagnósticos erróneos. opacity menor que 1, mix-blend-mode, isolation: isolate y position: fixed o sticky crean contexto de apilamiento pero no bloque contenedor: un position: fixed dentro de un opacity: .9 sigue anclado al viewport, aunque su apilamiento quede encerrado. Al revés, contain: layout, content-visibility: auto, transform, filter, perspective y backdrop-filter crean las dos. La razón de la diferencia es que son mecanismos distintos: el contexto de apilamiento aparece cuando el motor debe componer el subárbol como una unidad de pintado, y el bloque contenedor aparece cuando el elemento establece un nuevo sistema de coordenadas para sus descendientes. Una transformación hace las dos cosas porque cambia las coordenadas y obliga a componer aparte; una opacidad solo obliga a componer aparte. Cuando depures, pregúntate siempre cuál de los dos síntomas tienes: si el elemento aparece en el sitio equivocado, es bloque contenedor; si aparece en el sitio correcto pero por detrás, es apilamiento. Son bugs distintos con causas distintas y arreglos distintos.

⚔️ Determina el bloque contenedor a mano
  1. Coloca un absolute con inset: 0 dentro de un ancestro con padding: 2rem y mide cuánto ocupa. Explica el resultado.
  2. Quita el position: relative del ancestro y comprueba a qué caja salta el absoluto.
  3. Añade filter: blur(0) a un ancestro sin posicionar y verifica que el absoluto vuelve a él.
  4. Comprueba que padding-block-start: 50% produce una altura igual a la mitad de la anchura del bloque contenedor.
  5. Escribe un fixed dentro de un ancestro con opacity: .9 y demuestra que sigue anclado al viewport.