wandres.dev
GRID O FLEX · El árbol de decisión

El contenido manda o manda el layout

Flexbox parte del contenido y reparte lo que sobra; Grid parte de la rejilla y acomoda el contenido dentro. Aprende a leer un diseño para saber cuál de las dos direcciones describe el problema.

⏱ 17 min

Hay una segunda forma de plantear la elección entre Grid y Flexbox que es independiente de la dimensionalidad y a veces resuelve el empate: la dirección en la que fluye la información de tamaño. En Flexbox el contenido habla primero y el layout escucha; en Grid el layout habla primero y el contenido se acomoda. Los diseñadores llevan décadas llamando a esto content-out y layout-in, y saber cuál de los dos describe tu componente te ahorra la mitad de las discusiones.

🎯 Al terminar esta lección sabrás
  • Explicar por qué flex-basis: auto hace que el contenido sea el punto de partida del algoritmo.
  • Identificar en un diseño si las dimensiones vienen impuestas por la rejilla o negociadas por el contenido.
  • Usar las funciones de pista intrínsecas de Grid cuando necesitas que el contenido influya sin renunciar a la rejilla.
  • Detectar el síntoma del módulo equivocado: números mágicos que hay que retocar al cambiar el texto.

Content-out: el algoritmo empieza midiendo el contenido

El valor por defecto de flex es 0 1 auto, y ese auto final es la parte interesante. Significa que flex-basis toma el valor de width (o height en columna), y si ese valor también es auto, el tamaño base del elemento es su tamaño de contenido máximo: lo que ocuparía si nadie lo apretara. Flexbox suma esos tamaños base, los compara con el espacio disponible y solo entonces decide si hay que repartir sobrante con flex-grow o recortar déficit con flex-shrink.

El contenido no es un pasajero: es el dato de entrada del algoritmo. Por eso Flexbox brilla en todo lo que tenga un número o un texto de longitud imprevisible.

/* Un grupo de etiquetas: cada una mide lo que mide su texto */
.etiquetas {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.etiquetas > li {
  flex: 0 0 auto;          /* ni crece ni encoge: manda el texto */
  padding: 0.25rem 0.75rem;
  border-radius: 999px;
  background: color-mix(in oklch, currentColor 12%, transparent);
}

Escribir esto con Grid es posible —grid-auto-flow: column con pistas auto— pero es una traducción forzada: estás declarando una rejilla cuya única función es no imponer nada. Cuando el CSS que escribes se dedica a desactivar las características del módulo que has elegido, has elegido mal.

Layout-in: la rejilla existe antes que el contenido

El caso contrario es una plantilla de página, un calendario, una tabla de precios o un panel de control. Ahí las dimensiones no son negociables: la columna de la barra lateral mide 18rem porque el diseño dice 18rem, y el contenido tendrá que apañarse. Declarar la estructura por adelantado no es una limitación, es exactamente lo que quieres.

.panel {
  display: grid;
  grid-template-columns: 18rem minmax(0, 1fr);
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "lateral cabecera"
    "lateral principal"
    "lateral pie";
  min-block-size: 100dvh;
}
.panel > .lateral   { grid-area: lateral; }
.panel > .cabecera  { grid-area: cabecera; }
.panel > .principal { grid-area: principal; }
.panel > .pie       { grid-area: pie; }

Fíjate en minmax(0, 1fr) en la segunda columna. Es la formulación defensiva del 1fr: 1fr equivale a minmax(auto, 1fr), y ese mínimo auto significa “no bajes del tamaño mínimo del contenido”, lo que hace que una tabla ancha o una línea de código larga ensanchen la columna y desborden el panel entero. minmax(0, 1fr) corta esa contribución de raíz. Es el equivalente en Grid del min-width: 0 que ya conoces de Flexbox, y por la misma razón: el tamaño mínimo automático es una salvaguarda contra pérdida de datos que aquí estorba.

💡
El mismo bug con dos nombres

min-width: auto en un ítem flex y minmax(auto, 1fr) en una pista de grid son el mismo mecanismo: el motor se niega a encoger algo por debajo de lo que necesita para no cortar contenido. En los dos casos el remedio es declarar explícitamente que sí quieres permitirlo (min-width: 0, minmax(0, 1fr)) y encargarte tú del desbordamiento con overflow o text-overflow.

La zona intermedia: pistas intrínsecas

La dicotomía se rompe en cuanto descubres que Grid también sabe escuchar al contenido, solo que a través de la pista. auto, min-content, max-content, fit-content(20rem) y minmax(min-content, max-content) son funciones de dimensionado que hacen que una columna mida lo que necesite el elemento más ancho que la ocupe.

/* Formulario: la columna de etiquetas mide lo que la etiqueta mas larga */
.formulario {
  display: grid;
  grid-template-columns: max-content minmax(12rem, 1fr);
  gap: 0.75rem 1rem;
  align-items: baseline;
}

Esa columna max-content es content-out dentro de un layout-in: el ancho lo decide el contenido, pero es un ancho compartido por toda la columna, cosa que Flexbox no puede darte. Es el caso donde la respuesta correcta es Grid aunque tu instinto diga Flex, y es el mismo criterio de la lección anterior: existe una coordenada compartida.

La operación inversa, hacer que Flexbox imponga tamaños, siempre es más torpe. Puedes escribir flex: 0 0 18rem, pero eso fija el ancho en un elemento suelto, no declara una columna. Repite ese 18rem en tres componentes y ya tienes tres sitios donde tocarlo.

El síntoma diagnóstico

Cuando el módulo es el equivocado, aparece siempre el mismo olor: constantes que hay que retocar cuando cambia el contenido. Un padding-left: 232px para alinear con una barra lateral que mide 232px en otro fichero. Un flex-basis: 31.4% calculado para que quepan tres con su hueco. Un width: calc(33.333% - 0.667rem) que se rompe si añades un cuarto elemento.

Cada uno de esos números es una relación bidimensional escrita a mano porque el módulo elegido no sabía expresarla. La versión sana es que el número esté declarado una sola vez, en la definición de la pista, y que todo lo demás lo derive.

El CSS que envejece es el que codifica resultados en vez de intenciones

Hay una asimetría cruel en esto: los dos enfoques producen el mismo píxel el día que los escribes, y solo divergen seis meses después, cuando alguien mete un nombre de producto de cuarenta caracteres o traduce la interfaz al alemán. El CSS que dice flex-basis: 31.4% ha codificado el resultado de un razonamiento —“caben tres con gap de 1rem en un contenedor de tal ancho”— y ha tirado el razonamiento a la basura; nadie que lea ese fichero dentro de un año podrá reconstruirlo, y cualquier cambio en las premisas lo invalida en silencio, sin romper el build ni fallar ningún test. El CSS que dice repeat(auto-fit, minmax(12rem, 1fr)) ha codificado la intención —“tantas columnas como quepan, ninguna por debajo de 12rem”— y sigue siendo verdad con cuatro elementos, con cuarenta, en alemán y en un contenedor que nadie había previsto. Elegir entre content-out y layout-in no es una cuestión de gusto: es elegir en qué lenguaje vas a dejar escrita la única parte del diseño que el navegador puede verificar por ti.