El flujo normal: la solución que el motor da por defecto
Por qué un bloque llena el ancho y solo abraza el alto, de dónde sale esa asimetría entre los dos ejes y qué significa realmente pensar en dirección de bloque y de línea.
Antes de que elijas un modelo de layout, el motor ya tiene uno funcionando. El flujo normal no es el estado de partida sobre el que se construye lo bueno: es un algoritmo completo, pensado para documentos de texto, que resuelve correctamente el noventa por ciento de los casos si no lo estropeas. Y contiene una asimetría entre sus dos ejes que, una vez la ves, explica de golpe por qué los anchos son fáciles y las alturas dan problemas, por qué height: 100% casi nunca hace lo que crees y por qué centrar verticalmente fue durante veinte años un chiste recurrente.
- Describir el comportamiento por defecto de una caja de bloque en cada uno de los dos ejes.
- Explicar por qué el eje de línea se resuelve antes que el eje de bloque.
- Traducir propiedades físicas a sus equivalentes lógicos y decir cuándo importa.
- Diagnosticar un
heighten porcentaje que no surte efecto.
Lo que hace una caja cuando no le dices nada
Escribe un div sin una sola declaración y observa lo que ya está decidido: ocupa todo el ancho disponible de su contenedor, ocupa exactamente el alto que necesita su contenido, y se coloca debajo del elemento anterior. Tres decisiones, ninguna tuya.
La primera se llama dimensionado extrínseco: el tamaño lo dicta el contenedor. La segunda es dimensionado intrínseco: el tamaño lo dicta el contenido. Y ahí está lo que casi nadie enuncia explícitamente: una caja de bloque usa un criterio distinto en cada eje. Ancho hacia fuera, alto hacia dentro.
/* Estas dos declaraciones son redundantes: ya es lo que pasa */
.caja {
inline-size: auto; /* llena el contenedor */
block-size: auto; /* abraza el contenido */
}
Esa asimetría es la fuente de casi todas las diferencias prácticas entre trabajar con anchos y trabajar con alturas. Un width: 50% funciona siempre porque el ancho del contenedor está resuelto cuando le toca el turno a este elemento. Un height: 50% funciona solo a veces, y la condición es exactamente que el contenedor tenga una altura definida que no dependa a su vez de sus hijos. Si el contenedor tiene height: auto, su altura depende de la de sus hijos, y preguntarle a un hijo que se dimensione en porcentaje de algo que se calcula a partir de él mismo es una circularidad. La especificación la corta declarando que en ese caso el porcentaje se trata como auto.
/* No funciona: el padre no tiene altura definida */
.padre { }
.hijo { height: 50%; } /* se comporta como height: auto */
/* Funciona: el padre tiene altura definida */
.padre { block-size: 20rem; }
.hijo { block-size: 50%; } /* 10rem */
Nadie te va a avisar de la primera versión. No es un error, es una declaración perfectamente válida cuyo valor calculado acaba siendo auto. Este es, con diferencia, el “no funciona” más frecuente de CSS.
Por qué el motor no puede hacerlo de otra manera
La asimetría no es una convención histórica ni una comodidad: es una consecuencia de que el flujo se diseñó para texto, y el texto tiene una dependencia direccional inevitable.
Para maquetar un párrafo, el motor tiene que decidir dónde parte cada línea. Para decidir dónde parte cada línea, necesita saber cuánto mide la línea. Y una vez ha partido las líneas, y solo entonces, sabe cuántas hay y por tanto qué altura ocupa el párrafo. La cadena de dependencias es rígida:
ancho disponible → puntos de corte de línea → número de líneas → altura
El eje de línea es la entrada del algoritmo y el eje de bloque es la salida. No se pueden intercambiar sin cambiar el problema. Por eso el ancho puede venir dado desde fuera sin coste, y por eso la altura, en cambio, es el resultado de un cálculo que ya ha terminado. Cuando escribes una altura fija estás sobrescribiendo un resultado, no dando una entrada, y de ahí que las alturas fijas rompan cosas con tanta facilidad: el contenido puede necesitar más líneas de las que caben, y lo que sobra se sale.
De la misma cadena se sigue una regla operativa muy concreta: desconfía sistemáticamente de cualquier altura fija que contenga texto. Con otro tamaño de fuente del usuario, otro idioma más largo o una palabra que no cabe, esa caja desbordará. Si necesitas una altura mínima, min-block-size deja que el contenido crezca; una altura fija, no.
El chiste de los veinte años tiene una causa técnica exacta, y entenderla vale más que aprenderse la solución. Centrar horizontalmente siempre fue fácil porque el ancho del contenedor está definido antes de mirar al hijo: hay un sobrante conocido, y repartirlo entre dos márgenes auto es aritmética. Centrar verticalmente en el flujo normal era imposible porque no había sobrante: la altura del contenedor se calculaba a partir de la del hijo, de modo que el hueco vertical valía cero por construcción. No faltaba una propiedad, faltaba el número sobre el que operar. Todos los trucos históricos —la tabla, el line-height igual a la altura, el translateY(-50%) combinado con top: 50%, el pseudo-elemento con vertical-align: middle— eran maneras distintas de fabricar artificialmente una altura de referencia. Flexbox y Grid resolvieron el chiste no porque trajeran una propiedad de centrado, sino porque introdujeron algo mucho más profundo: contextos de layout en los que el contenedor tiene una altura definida antes de colocar a sus hijos, y por tanto existe un sobrante que repartir. place-content: center es la misma aritmética de siempre aplicada a un número que en el flujo normal no existía. Cuando alguien te diga que CSS ya no es raro porque ahora se puede centrar, tienes la respuesta precisa: no cambió la dificultad de centrar, cambió el orden en que se resuelven las dimensiones.
Bloque y línea, no vertical y horizontal
Hasta aquí he escrito “ancho” y “alto” para que se entendiera, pero la especificación moderna no razona así, y hay una razón que no es purismo.
El flujo tiene dos direcciones definidas relativas al modo de escritura, no a la pantalla. La dirección de línea es aquella en la que se suceden las palabras; la de bloque, aquella en la que se apilan las líneas y los bloques. En español y en inglés eso da línea horizontal y bloque vertical, así que coinciden con ancho y alto. En japonés vertical tradicional, con writing-mode: vertical-rl, la línea es vertical y el bloque horizontal, y todo lo anterior sigue siendo cierto palabra por palabra si usas el vocabulario lógico.
| Físico | Lógico | Nota |
|---|---|---|
width |
inline-size |
El eje de la lectura |
height |
block-size |
El eje del apilado |
margin-top |
margin-block-start |
Inicio del eje de bloque |
margin-left | margin-right |
margin-inline-start | margin-inline-end |
Depende de la dirección |
padding: 0 1rem |
padding-inline: 1rem |
Ambos lados del eje de línea |
text-align: left |
text-align: start |
Sigue a la dirección del texto |
La ventaja concreta de escribir en lógico no es soportar japonés vertical, que probablemente nunca necesites. Es que un sitio en árabe o hebreo funciona sin tocar una línea: margin-inline-start se voltea solo cuando direction es rtl, mientras que margin-left se queda donde estaba y te obliga a mantener una hoja espejada. Y hay una segunda ventaja más sutil: escribir en lógico te obliga a pensar en términos del algoritmo que de verdad se está ejecutando, y eso hace que la asimetría de la que va esta lección se vuelva visible en el propio código.
Trabajar con el flujo, no contra él
El flujo normal tiene una propiedad que los layouts explícitos no tienen: es el que mejor absorbe lo imprevisto. Un documento en flujo con anchos automáticos y alturas automáticas sobrevive a cualquier cantidad de contenido, a cualquier tamaño de fuente y a cualquier ventana, porque no hay nada que pueda entrar en conflicto. Cada restricción que añades encima le quita una parte de esa capacidad.
De ahí tres criterios que valen para todo lo que venga después.
No abandones el flujo por costumbre. Un artículo de texto no necesita Grid. Una lista vertical de tarjetas de ancho completo no necesita Flexbox. Poner un contexto de layout donde no hace falta añade reglas de alineación, de reparto y de tamaño intrínseco que luego tienes que gestionar.
Cuando lo abandones, sepas por qué. El motivo legítimo suele ser uno de tres: necesitas repartir espacio sobrante, necesitas alinear en el eje de bloque, o necesitas relación entre elementos que en el flujo no se ven.
Vuelve al flujo dentro de cada componente. Un patrón muy eficaz consiste en usar Grid o Flexbox solo en el contenedor de más alto nivel y dejar que el interior de cada pieza sea flujo puro. Cuanto más profunda es la anidación de contextos de layout, más difícil es predecir de dónde sale un tamaño.
En la lección siguiente desarmamos la palabra que ha aparecido veinte veces en esta y que casi nadie sabe leer con precisión: auto.