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.
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.
- Enumerar los disparadores de un BFC y el efecto secundario de cada uno.
- Justificar por qué
display: flow-rootes 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.
El catálogo
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.
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;
}
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.