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

El IFC y la caja de línea

El contexto de formato en línea: cómo se construye una línea de texto, qué es el strut, por qué hay un hueco bajo las imágenes y de dónde salen los espacios que nadie escribió.

⏱ 20 min

El contexto de formato en línea es el algoritmo con el que el navegador convierte una secuencia de texto y elementos en línea en un montón de rectángulos apilados. Es el más antiguo de CSS, el que más se usa —cada párrafo de la web pasa por él— y el peor entendido, porque su unidad fundamental, la caja de línea, no existe en el DOM y no se puede seleccionar con ningún selector. De ahí salen dos de los enigmas más citados de la historia del CSS.

🎯 Al terminar esta lección sabrás
  • Describir cómo se construye una caja de línea y qué la dimensiona.
  • Explicar el strut y su papel en la altura de una línea vacía.
  • Diagnosticar el hueco bajo una imagen y resolverlo de tres formas.
  • Explicar de dónde salen los espacios entre elementos inline-block.

Cómo se construye una línea

Un contexto de formato en línea aparece dentro de un contenedor de bloque cuando todos sus hijos son contenido en línea: texto, span, a, img, code. El motor no coloca elementos: coloca cajas en línea dentro de cajas de línea.

El proceso es este. El motor toma el contenido en línea y lo va midiendo hasta que no cabe más en el ancho disponible. Cierra ahí una caja de línea, empieza otra, y repite. Cada caja de línea es un rectángulo tan alto como haga falta para contener todas las cajas en línea que lleva dentro, alineadas por su línea base.

Las consecuencias inmediatas son tres, y ninguna es obvia. Una caja de línea no es un elemento: si un span se parte en dos líneas, produce dos fragmentos en dos cajas de línea distintas, y no puedes estilarlos por separado. El ancho de una caja de línea lo fija el contenedor, no el contenido. Y la altura la fija el contenido, con una excepción importante que viene ahora.

El strut, el fantasma que mide la línea

Cada caja de línea contiene, además del contenido real, una caja en línea invisible y de anchura cero que hereda la tipografía del contenedor de bloque. La especificación la llama strut. Existe siempre, aunque la línea esté vacía de texto.

El strut es la razón de que un div vacío con line-height: 1.5 y font-size: 16px tenga altura, y de que cambiar la fuente de un contenedor cambie la altura de sus líneas aunque el texto de dentro tenga otra fuente distinta.

/* La línea mide al menos 24px por el strut, aunque el span sea diminuto */
.caja { font-size: 16px; line-height: 1.5; }
.caja span { font-size: 8px; line-height: 1; }

Aquí está también la explicación del comportamiento que más desconcierta de line-height: el espacio extra que introduce se reparte por igual arriba y abajo de cada caja en línea, mitad y mitad. Ese reparto se llama half-leading, viene directo de la tipografía de plomo, y es el motivo de que un line-height menor que 1 corte las mayúsculas por arriba y por abajo simétricamente en lugar de solo por abajo.

📝
line-height sin unidad, siempre

line-height: 1.5 y line-height: 150% no son lo mismo. El porcentaje se resuelve a un valor absoluto en el elemento donde lo escribes y ese píxel concreto es lo que heredan los descendientes; el número sin unidad se hereda como número y cada descendiente lo multiplica por su propio font-size. Con porcentaje, un hijo con letra grande hereda una altura de línea calculada para letra pequeña y las líneas se solapan.

El hueco bajo la imagen

El enigma más famoso del CSS. Una imagen dentro de un contenedor deja un espacio de tres o cuatro píxeles por debajo que no viene de ningún margen.

<div class="marco"><img src="/foto.jpg" alt=""></div>
.marco { background: #f38ba8; }
img { display: block; } /* quítalo y aparece el hueco */

La causa no es un margen: es que img es un elemento en línea por defecto, y por tanto se alinea con vertical-align: baseline. La línea base de una caja de línea no está en su borde inferior, sino donde se apoyan las letras sin descendentes. El hueco que ves es exactamente el espacio reservado para los descendentes de la fuente —la cola de la p, de la g, de la j— que el strut sigue reclamando aunque no haya ni una letra en la línea.

Las tres soluciones válidas atacan tres puntos distintos del mecanismo:

/* 1. Sacar la imagen del IFC: ya no se alinea a ninguna línea base */
img { display: block; }

/* 2. Cambiar la alineación: pegarla al borde inferior de la línea */
img { vertical-align: bottom; }

/* 3. Eliminar el descendente: sin altura de línea no hay hueco */
.marco { line-height: 0; }

La primera es la que debes usar por defecto en un componente. La segunda es la correcta cuando la imagen tiene que seguir fluyendo con texto. La tercera funciona pero contamina la herencia y obliga a restaurar line-height en el contenido.

Los espacios que nadie escribió

Segundo enigma clásico, y consecuencia directa del mismo modelo. Tres elementos inline-block puestos uno detrás de otro en el HTML aparecen separados por un espacio que no está en ninguna hoja de estilos.

<nav>
  <a class="pastilla">Uno</a>
  <a class="pastilla">Dos</a>
</nav>
.pastilla { display: inline-block; padding: 0.5rem 1rem; background: #89b4fa; color: #11111b; }

El espacio está en el HTML: es el salto de línea y la indentación entre las dos etiquetas. En un contexto en línea, el espacio en blanco es contenido significativo, porque el IFC se diseñó para maquetar prosa y en la prosa los espacios separan palabras. El motor colapsa la secuencia de espacios y saltos en un único espacio, y lo pinta con el tamaño de la fuente del contenedor.

Los apaños históricos son todos malos: escribir el HTML sin saltos de línea, comentar el hueco con <!-- -->, o poner font-size: 0 en el padre y restaurarlo en los hijos. El arreglo real es no estar en un contexto en línea:

nav { display: flex; gap: 0.5rem; }
.pastilla { padding: 0.5rem 1rem; background: #89b4fa; color: #11111b; }

En un contexto flex, el espacio en blanco entre elementos no genera ningún elemento anónimo y desaparece por completo. Que este bug haya sido irrelevante desde 2017 no es una casualidad: es que dejamos de maquetar interfaces con el algoritmo de la prosa.

El IFC es un motor tipográfico, y por eso te sorprende

La clave para dejar de pelearse con el contexto en línea es aceptar que no es un sistema de layout: es un motor de composición tipográfica al que le hemos pedido cosas para las que no se hizo. Cada rareza tiene una explicación en la caja de plomo. El half-leading reparte el interlineado a ambos lados porque un tipógrafo insertaba láminas de plomo entre líneas y el reparto simétrico era lo natural. El strut existe porque una línea vacía en una columna de texto tiene que ocupar exactamente lo mismo que una llena, o el ritmo vertical se descuadra. La línea base es el eje de alineación porque en tipografía las letras se apoyan sobre una recta, no sobre el borde inferior de nada. El hueco bajo la imagen es el espacio para las colas de las letras. Ninguno de esos comportamientos es un bug; son el comportamiento correcto de un sistema de composición aplicado a un contenido que no es texto. Cuando compones prosa, el IFC es asombroso y hace exactamente lo que quieres. Cuando maquetas una barra de navegación con él, estás pidiéndole a una imprenta que te construya una estantería. La lección práctica no es memorizar los tres arreglos del hueco de la imagen, sino aprender a detectar cuándo estás usando el motor equivocado, porque en ese caso ningún arreglo es bueno: solo el cambio de contexto lo es.