wandres.dev
CONTEXTOS DE FORMATO · BFC, IFC y el flujo normal

El BFC y el catálogo completo de lo que lo crea

La lista exhaustiva de disparadores de un contexto de formato de bloque, ordenada por efectos secundarios, y por qué display flow-root es el único que hace su trabajo sin cobrarte nada a cambio.

⏱ 20 min

Durante quince años, la forma de arreglar un layout roto fue añadir overflow: hidden a un contenedor y rezar. Funcionaba, y nadie sabía muy bien por qué. Funcionaba porque overflow distinto de visible es uno de los ocho o nueve disparadores de un contexto de formato de bloque, y ese era el efecto que buscabas; el recorte del contenido era el peaje que pagabas sin saberlo. Conocer el catálogo completo te deja elegir el disparador cuyo peaje puedes permitirte.

🎯 Al terminar esta lección sabrás
  • Enumerar los disparadores de un BFC y el efecto secundario de cada uno.
  • Justificar por qué display: flow-root es el disparador por defecto desde 2018.
  • Explicar por qué flex y grid establecen contextos que no son BFC pero se comportan igual hacia fuera.
  • Elegir el disparador correcto según lo que el contenedor tenga que seguir haciendo.

Una caja establece un contexto de formato de bloque para sus descendientes cuando se cumple cualquiera de estas condiciones. La lista viene de CSS Display 3 y de CSS Overflow 3, y es cerrada: si tu caja no está aquí, no abre un BFC.

disparador efecto secundario que pagas
es el elemento raíz ninguno, es el BFC inicial
float distinto de none la caja sale del flujo y se pega a un lado
position: absolute o fixed la caja sale del flujo por completo
display: inline-block la caja pasa a participar en línea
display: table-cell, table-caption comportamiento de tabla, ancho impredecible
overflow distinto de visible y de clip scroll o recorte del contenido
display: flow-root ninguno
celdas y captions de tabla anónimos los genera el motor, no tú
column-count o column-width el contenido se reparte en columnas
column-span: all solo tiene sentido dentro de multicolumna
contain: layout, content o paint aísla también el pintado o el tamaño
display: flex, grid, inline-flex, inline-grid sus hijos dejan de ser cajas de bloque

Léela como lo que es: una lista de efectos que alguien quería, cada uno con un aislamiento como consecuencia colateral. Ninguno de los diez primeros se diseñó para crear un BFC. Se diseñaron para flotar, para recortar, para columnar. El BFC venía de regalo porque la especificación no tenía otra forma coherente de definir el resultado.

flowchart TB
T[Que abre un contexto de formato de bloque] --> A[Elemento raiz]
T --> B[float distinto de none]
T --> C[position absolute o fixed]
T --> D[display inline-block]
T --> E[overflow distinto de visible y de clip]
T --> F[display flow-root]
T --> G[Contenedor flex o grid]
T --> H[Celda o caption de tabla]
T --> I[Contenedor multicolumna]
T --> J[contain layout paint o content]
B --> BX[Sale del flujo]
C --> CX[Sale del flujo]
D --> DX[Participa en linea]
E --> EX[Recorta o hace scroll]
F --> FX[Sin efecto secundario]
G --> GX[Sus hijos dejan de ser bloques]
I --> IX[Reparte el contenido en columnas]
J --> JX[Aisla tambien pintado o tamano]
style T fill:#cba6f7,color:#11111b
style F fill:#a6e3a1,color:#11111b
style FX fill:#a6e3a1,color:#11111b
style BX fill:#f9e2af,color:#11111b
style CX fill:#f9e2af,color:#11111b
style DX fill:#f9e2af,color:#11111b
style EX fill:#f9e2af,color:#11111b
style GX fill:#f9e2af,color:#11111b
style IX fill:#f9e2af,color:#11111b
style JX fill:#f9e2af,color:#11111b

flow-root: el disparador sin factura

display: flow-root se añadió a CSS Display 3 precisamente porque la comunidad llevaba una década usando efectos secundarios como si fueran API. El nombre es literal: la caja se convierte en la raíz de un flujo nuevo. Nada más. Sigue siendo un bloque, sigue ocupando el ancho disponible, sigue permitiendo que su contenido desborde visiblemente, y sus hijos siguen siendo cajas de bloque en flujo normal.

/* El clearfix de 2010, con toda su deuda */
.contenedor::after { content: ""; display: table; clear: both; }

/* El clearfix de 2015, que además recorta lo que no querías recortar */
.contenedor { overflow: hidden; }

/* Lo mismo, sin peaje */
.contenedor { display: flow-root; }

Está disponible en todos los motores desde 2018 y no tiene ninguna trampa conocida. Si lo único que necesitas es aislamiento, es la respuesta correcta y no hay debate.

💡
Cuándo sigue teniendo sentido overflow

Usa overflow cuando de verdad quieras el scroll o el recorte, no como sinónimo de aislamiento. Un caso legítimo: un panel lateral que debe hacer scroll interno. Ahí overflow: auto te da el BFC gratis y el scroll que buscabas. Otro caso legítimo: overflow: clip recorta pero no crea un BFC ni una caja desplazable, que es justo lo que quieres cuando solo necesitas recortar y no quieres cambiar el layout.

Flex y grid no crean un BFC, pero se comportan como si lo hicieran

Aquí hay un matiz que la mayoría de los artículos se salta. Un contenedor flex no establece un contexto de formato de bloque: establece un contexto de formato flex. Un contenedor grid establece un contexto de formato grid. Sus hijos no se maquetan con el algoritmo de bloque, sino con el de flex o el de grid, así que llamarlos BFC sería falso.

Lo que sí ocurre es que la especificación define ambos como contextos de formato independientes, y les asigna las mismas garantías hacia fuera que a un BFC: los flotantes no entran, los márgenes no colapsan con el contenido, y la caja se dimensiona alrededor de todo lo suyo. Por eso el overflow: hidden que ponías en el contenedor para “arreglar” un layout deja de hacer falta en cuanto ese contenedor pasa a ser flex.

/* Las tres tienen el mismo aislamiento frente al exterior */
.a { display: flow-root; }   /* BFC */
.b { display: flex; }        /* contexto flex, independiente */
.c { display: grid; }        /* contexto grid, independiente */

Además, cada elemento flex y cada elemento grid establece a su vez un contexto de formato independiente para su propio contenido. Es decir: en cuanto usas flex o grid, cortas la cadena de colapsos de márgenes en dos sitios a la vez, en el contenedor y en cada hijo. Ese es el motivo real de que los layouts modernos parezcan menos frágiles que los de 2012: no es que flex sea más listo, es que aísla mucho más.

Elegir con criterio

La pregunta que hay que hacerse no es “cómo creo un BFC” sino “qué tiene que seguir pudiendo hacer este contenedor”.

Si el contenedor debe seguir mostrando contenido que desborde, como un tooltip absoluto que sobresale del borde, overflow: hidden está descartado y flow-root es la única opción limpia. Si el contenedor debe hacer scroll, overflow: auto mata dos pájaros. Si ya es flex o grid por razones de layout, no añadas nada: ya tienes el aislamiento. Si necesitas además aislar el coste de recálculo del layout, contain: layout te da el BFC y le promete al motor que nada de dentro afecta a nada de fuera.

/* Tarjeta con una insignia que sobresale por la esquina.
   overflow: hidden la decapitaría; flow-root no. */
.tarjeta {
  display: flow-root;
  position: relative;
  padding: 1rem;
}
.tarjeta .insignia {
  position: absolute;
  inset-block-start: -0.5rem;
  inset-inline-end: -0.5rem;
}
Todo el CSS de layout es una historia de efectos secundarios promovidos a API

El catálogo de disparadores de BFC es un fósil, y leerlo como tal enseña más que memorizarlo. Ninguna de esas condiciones fue diseñada para lo que hoy usamos. overflow: hidden crea un BFC porque una caja con scroll tiene que saber dónde acaba su contenido, y para saberlo tiene que contar sus flotantes; el aislamiento es una consecuencia matemática de poder calcular una barra de scroll. float crea uno porque un flotante dentro de otro flotante habría sido indefinible de otro modo. Durante quince años, la comunidad explotó esas consecuencias como si fueran contratos, y la especificación acabó cediendo: display: flow-root es la confesión formal de que el efecto secundario era en realidad la funcionalidad que todo el mundo quería. La lección que se generaliza es que cuando un patrón se usa masivamente por su efecto colateral, el estándar acaba dándole un nombre propio. flow-root es a overflow: hidden lo que gap es al margen negativo, y lo que :has() es al hack del hermano adyacente. Aprende a distinguir en tu propio código cuáles son contratos y cuáles son consecuencias: los primeros sobreviven a las refactorizaciones y los segundos no.