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

Una dimensión frente a dos: qué distingue de verdad a Grid de Flex

La diferencia entre Flexbox y Grid no es estética ni de tamaño de layout: es si los elementos comparten o no un sistema de coordenadas común. Todo lo demás se deriva de ahí.

⏱ 18 min

La pregunta “¿esto lo hago con Grid o con Flex?” está mal planteada casi siempre, porque presupone que son dos herramientas intercambiables que resuelven el mismo problema con distinta sintaxis. No lo son. Flexbox distribuye espacio libre a lo largo de un eje y trata cada línea como un universo aislado; Grid resuelve un sistema de pistas en dos ejes y coloca a todos los elementos sobre las mismas coordenadas. Esa única diferencia estructural explica los cuatro o cinco comportamientos concretos por los que siempre acabas eligiendo uno u otro.

🎯 Al terminar esta lección sabrás
  • Explicar qué significa “una dimensión” en términos del algoritmo, no de la apariencia.
  • Demostrar con código por qué la última fila de un flex con wrap no se alinea con las anteriores.
  • Distinguir el reparto de espacio libre de Flexbox del dimensionado de pistas de Grid.
  • Reconocer, ante un diseño dado, si existe o no una estructura de pistas compartida.

Qué significa “una dimensión” en el algoritmo

Flexbox coloca los elementos a lo largo del eje principal, uno detrás de otro, y cuando permites flex-wrap: wrap los reparte en líneas. La clave está en lo que ocurre después: cada línea de flex resuelve su propio reparto de espacio de forma independiente. Una línea no sabe nada de la anterior. Si la primera línea acabó con tres elementos de 200px y la segunda tiene dos, la segunda repartirá su espacio libre entre esos dos y punto: no existe ninguna estructura que obligue a que el segundo elemento de la línea 2 empiece donde empieza el segundo elemento de la línea 1.

Grid hace lo contrario y lo hace antes. Primero resuelve el algoritmo de dimensionado de pistas: decide cuánto miden las columnas y las filas del contenedor entero, y solo después coloca los elementos en las celdas resultantes. Las pistas son globales. El elemento que cae en la columna 2 de la fila 5 empieza exactamente en la misma línea vertical que el de la columna 2 de la fila 1, porque literalmente es la misma línea de rejilla.

De ahí sale la definición operativa que sí sirve: Grid existe cuando hay un sistema de coordenadas compartido; Flex existe cuando no lo hay. No tiene nada que ver con si el layout es grande o pequeño, de página o de componente.

El experimento que lo demuestra

El mismo marcado, siete tarjetas, dos contenedores. Copia esto y redimensiona la ventana:

.flex {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}
.flex > * {
  flex: 1 1 12rem; /* crecer, encoger, base de 12rem */
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 1rem;
}

En el contenedor flex, cuando caben tres por línea, la última línea con una sola tarjeta la verás ocupando el 100% del ancho: esa tarjeta tiene flex-grow: 1 y en su línea es la única que compite por el espacio libre, así que se lo queda todo. En el contenedor grid, la tarjeta suelta de la última fila mide exactamente lo que mide una columna, porque la columna ya estaba definida antes de que llegara.

Ninguno de los dos está roto. Son dos respuestas correctas a dos preguntas distintas: “reparte el espacio sobrante de esta línea” frente a “coloca esto en la pista 1”. Si lo que quieres es una galería regular, la respuesta de Grid es la que buscas. Si lo que quieres es una barra de etiquetas que llene el ancho sin huecos, la de Flex.

⚠️
El truco de los ítems fantasma

El apaño clásico para “arreglar” la última línea de un flex es añadir elementos vacíos invisibles con flex-basis igual al de los reales. Funciona, ensucia el DOM y se rompe en cuanto cambias el número de columnas. Si estás escribiendo ese apaño es que el layout tenía pistas compartidas desde el principio y elegiste mal el módulo.

Envolver no crea una segunda dimensión

Es tentador pensar que flex-wrap: wrap convierte a Flexbox en bidimensional, porque el resultado visual tiene filas y columnas. No es así. Lo que hace flex-wrap es generar varias líneas de flex, y lo único que puedes controlar globalmente sobre ellas es cómo se distribuyen en el eje transversal con align-content. Puedes decidir si las líneas se pegan arriba, se centran o se separan; no puedes decidir que la tercera línea sea más alta que las demás, ni que un elemento ocupe dos líneas, ni que las columnas visuales que se forman tengan un ancho común.

Grid, en cambio, te da esa segunda dimensión de forma explícita: grid-row: span 2 es una operación legal porque las filas existen como entidades nombrables. En Flexbox no hay ninguna propiedad equivalente, y no la hay por diseño: no se puede abarcar algo que no existe en el modelo.

Hay una excepción interesante que confirma la regla. La alineación de líneas base sí cruza el límite: dos elementos flex de la misma línea pueden alinear su primera línea de texto con align-items: baseline, y eso es una coordenada compartida. Pero es compartida solo dentro de la línea, otra vez.

Qué optimiza cada algoritmo

Flexbox resuelve un problema de reparto: parte de un tamaño base para cada elemento (flex-basis, que por defecto es auto y por tanto viene del contenido), suma, compara con el espacio disponible y reparte la diferencia según flex-grow o flex-shrink. Es un algoritmo de una pasada por línea sobre cantidades escalares. Por eso Flexbox es tan bueno cuando el contenido es de tamaño desconocido: el contenido entra en la ecuación como dato de partida.

Grid resuelve un problema de dimensionado de pistas, que es más caro: hay que determinar las contribuciones mínimas y máximas de cada elemento a cada pista que ocupa, resolver las pistas intrínsecas, y solo entonces distribuir el espacio libre entre las pistas de fr. El contenido influye —min-content, max-content, auto y fit-content() son valores de pista perfectamente válidos— pero influye a través de la pista, no directamente sobre el elemento.

/* Flex: el elemento negocia su tamaño */
.barra { display: flex; gap: 0.5rem; }
.barra .titulo { flex: 1 1 auto; min-width: 0; } /* absorbe lo que sobra */
.barra .acciones { flex: 0 0 auto; }             /* mide lo que mide */

/* Grid: la pista impone el tamaño y el elemento se acomoda */
.cabecera {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.5rem;
}

Los dos ejemplos producen la misma cabecera. La diferencia aparece cuando hay una segunda cabecera debajo y quieres que la columna de acciones tenga el mismo ancho en ambas: con Grid basta con que compartan el contenedor de rejilla; con Flex no hay forma de conseguirlo sin fijar un ancho a mano.

La dimensionalidad es una propiedad del diseño, no del CSS

El error de raíz no es elegir mal el módulo: es no haberse preguntado nunca si el diseño tiene o no una estructura de pistas. Coge cualquier maqueta y hazle una sola pregunta: si muevo un borde de este elemento, ¿tiene que moverse el borde de algún elemento que no sea vecino suyo en la misma línea? Si la respuesta es sí, existe una coordenada compartida y necesitas Grid; ninguna combinación de flex-basis y porcentajes va a mantener esa relación cuando cambie el contenido, porque en el modelo de Flexbox esa relación no se puede expresar. Si la respuesta es no, Grid te va a obligar a inventar pistas que el diseño no tiene, y las vas a acabar recalculando cada vez que el contenido crezca. Los layouts que se pudren con el tiempo casi siempre son layouts donde alguien expresó una relación bidimensional con herramientas de una dimensión y la sostuvo a base de números mágicos. El módulo correcto es el que puede expresar la restricción del diseño, no el que consigue imitar el resultado hoy con este contenido concreto.

⚔️ Clasifica cinco layouts
  1. Coge cinco componentes reales de un proyecto tuyo y, para cada uno, responde a la pregunta del callout anterior antes de mirar el CSS que tienen.
  2. Reproduce el experimento de las siete tarjetas y comprueba el ancho de la última con las herramientas de desarrollo en los dos contenedores.
  3. Intenta hacer que un elemento flex ocupe dos líneas. Documenta por qué no puedes.
  4. Escribe la cabecera del ejemplo con Grid y duplícala: verifica con el inspector que la columna auto mide lo mismo en las dos.
  5. Cambia flex: 1 1 12rem por flex: 0 1 12rem y explica exactamente qué cambia en la última línea y por qué.