Estás montando un dashboard: un header, un sidebar, un área de contenido principal y un footer. Recurres a Flexbox porque es la herramienta de layout que conoces, y veinte minutos después tienes tres contenedores display: flex anidados, un sidebar que no mantiene su ancho sin un número mágico, y un footer que flota a mitad de página cuando el contenido es corto. Nada de eso es un bug de Flexbox. Estás usando una herramienta unidimensional para un problema bidimensional.
Flexbox distribuye el espacio a lo largo de una sola línea, ya sea una fila o una columna. Grid coloca los elementos en filas y columnas a la vez, y esas filas y columnas se relacionan entre sí. La mayor parte de la confusión entre Grid y Flexbox viene de saltarse esa distinción.
Flexbox es unidimensional, Grid es bidimensional
Los elementos flex viven en un solo eje, definido por flex-direction. Con flex-direction: row cada hijo se alinea de izquierda a derecha (o de derecha a izquierda, con row-reverse); con column se apilan de arriba abajo. Existe un eje transversal, y align-items actúa sobre él. Pero Flexbox nunca le pide a un elemento de una línea que se alinee con un elemento de otra. Incluso cuando los elementos hacen wrap, cada línea resultante se calcula de forma independiente.
| |
Los elementos de Grid se ubican en filas y columnas que defines de antemano, y puedes posicionar y dimensionar cada elemento respecto a ambos ejes a la vez.
| |
1fr es una unidad de fracción. El navegador resta primero los tracks fijos (240px) y luego reparte lo que queda entre los tracks fr. Así, grid-template-columns: 1fr 2fr le da a la segunda columna el doble de la porción del espacio sobrante que a la primera, no el doble de su ancho total.
Cómo construir un navbar con Flexbox
Un navbar es el ejemplo de manual del layout unidimensional: un logo, unos links y un botón en una sola línea, con el espacio repartido entre ellos.
| |
| |
justify-content: space-between fija el logo al borde izquierdo y el botón al derecho, con la lista de links en medio. align-items: center mantiene los tres centrados verticalmente aunque el logo sea más alto que el botón. La lista de links es su propio contenedor flex, así que su espaciado viene de gap y nunca tocas un margin.
Si reduces el viewport, nada hace wrap, porque flex-wrap por defecto es nowrap: los elementos se encogen hasta su ancho mínimo de contenido y luego desbordan el navbar. Si añades flex-wrap: wrap, los elementos que no entran caen a una segunda línea. Esa segunda línea se calcula de forma independiente y no se alinea con nada de la primera. Para un navbar, no pasa nada. Para un layout donde las filas tienen que alinearse entre sí, es la señal de que has llegado al límite de lo que Flexbox puede hacer.
Cómo construir un dashboard con CSS Grid
Los grid areas con nombre te permiten leer el layout de una página sin traducir números de fila y columna en tu cabeza.
| |
Cada nombre en grid-template-areas tiene que formar un rectángulo, y las cadenas son literalmente un dibujo de la página: basta mirar el CSS para ver que el sidebar ocupa las tres filas. El sidebar tiene un ancho fijo de 240px, el header y el footer reciben alturas explícitas, y main ocupa lo que queda porque su fila es 1fr. El footer queda pegado al fondo del viewport incluso en una página corta, sin posicionamiento absoluto ni JavaScript, porque min-height: 100vh en el contenedor más una fila 1fr ya lo consiguen.
La versión en Flexbox de la misma página necesita dos contenedores: una fila flex para sidebar más contenido, y luego una columna flex dentro del contenido para header, main y footer, con flex: 1 en main para empujar el footer hacia abajo. Funciona, pero estás apilando dos layouts unidimensionales para simular uno bidimensional, y cada cambio en la estructura de la página implica tocar los dos.
Un grid de tarjetas responsive sin media queries
Este patrón elimina los media queries para este caso: un grid de tarjetas que añade o quita columnas según cambia el viewport.
| |
repeat(auto-fit, minmax(220px, 1fr)) le dice al navegador que meta tantas columnas de al menos 220px como permita la fila, y que luego las estire para llenarla. Un contenedor de 1400px obtiene cinco columnas de unos 260px; un móvil obtiene una sola columna a ancho completo. Ningún breakpoint en ninguna parte.
auto-fit y auto-fill solo se diferencian cuando tienes menos tarjetas de las que llenarían una fila. auto-fit colapsa los tracks vacíos, así que las tarjetas reales se estiran para ocupar toda la fila. auto-fill mantiene los tracks vacíos, así que las tarjetas conservan el ancho que tendrían en una fila completa y el resto queda en blanco. Usa auto-fill cuando el ancho de la tarjeta debe mantenerse constante sin importar la cantidad, como un grid de productos donde tres resultados no deberían inflarse hasta ocupar un tercio de la página cada uno. Usa auto-fit cuando una fila a medio llenar se vea peor que unas tarjetas anchas, como un dashboard con dos o tres tiles de resumen.
Cómo combinar Grid y Flexbox
Casi nunca eliges uno solo para toda la página. Grid se encarga del esqueleto; Flexbox se encarga del contenido unidimensional dentro de cada región.
| |
Cada .card ocupa una celda del grid y se estira hasta la altura de la celda, porque los elementos de grid se estiran por defecto. Dentro de ella, header, body y footer forman una sola columna, así que justify-content: space-between pone el footer al fondo de cada tarjeta sin importar cuánto texto tenga el body. Esa es la regla que funciona en la práctica: Grid para todo lo que tiene que relacionar una fila y una columna a la vez, Flexbox para el contenido que corre a lo largo de una línea, anidado dentro de donde Grid lo haya colocado.
Si usas Grid para cambiar entre una vista de tarjetas y una vista de lista, guarda la elección del visitante en localStorage. Es una sola llamada a setItem, y la elección sobrevive a recargas y pestañas cerradas sin un viaje de ida y vuelta al servidor.
Cuándo usar subgrid para grids anidados
Un grid dentro de un elemento de grid empieza su propio conjunto de tracks, sin relación con los del padre. Eso se nota en cuanto quieres que las filas internas de una tarjeta (imagen, título, descripción, precio) se alineen a lo largo de una fila de tarjetas. Si fijas grid-template-rows: subgrid (o grid-template-columns: subgrid) en el grid anidado, este reutiliza los tracks del padre en vez de definir unos nuevos, así que la fila de título de cada tarjeta se alinea con la de sus vecinas aunque cada tarjeta siga siendo su propio contenedor de grid. Subgrid funciona en todos los navegadores principales actuales, así que puedes usarlo directamente en lugar de forzar alturas iguales con valores fijos.
Cuándo Flexbox es la mejor opción aunque Grid también podría hacerlo
Grid puede organizar una sola fila con grid-auto-flow: column. Pero para contenido que es realmente unidimensional, Flexbox tiene algo que Grid no tiene: flex-grow y flex-shrink permiten que cada elemento reclame más o menos del espacio sobrante según su propio contenido. Eso es justo lo que necesitas para una barra de búsqueda que debe ocupar todo el espacio que le dejen libre un botón y un icono de ancho fijo. Por defecto usa Grid cuando filas y columnas necesitan relacionarse; usa Flexbox cuando estás maquetando una sola línea y quieres crecimiento y encogimiento por elemento, no solo alineación.
El error también ocurre al revés. Hay quien construye un layout de varias columnas con flex-wrap: wrap más un width en porcentaje en cada elemento, para simular un grid. Funciona hasta que la última fila tiene menos elementos que las demás. Entonces space-between manda los dos últimos elementos a extremos opuestos, o flex-grow los estira más anchos que todo lo de arriba. El navegador sigue distribuyendo elementos a lo largo de líneas, no rellenando un grid. Si estás contando columnas, usa Grid.
Si además necesitas JavaScript en el resize, por ejemplo para reposicionar un tooltip junto a una tarjeta, aplícale debounce al handler en vez de ejecutarlo en cada evento de resize que dispara el navegador.
Grid o Flexbox: cómo decidir
Una sola pregunta lo resuelve: ¿los elementos tienen filas y columnas que deben alinearse entre sí, o es una sola línea de elementos?
| Situación | Usa |
|---|---|
| Navbar, grupo de botones, toolbar | Flexbox |
| Esqueleto de página: header, sidebar, main, footer | Grid |
| Grid responsive de tarjetas o productos | Grid (auto-fit/auto-fill + minmax) |
| Centrar un elemento, o repartir espacio entre unos pocos | Flexbox |
| Formulario con labels e inputs alineados | Grid |
| Contenido dentro de una sola celda de grid (header/body/footer de una tarjeta) | Flexbox, anidado dentro del grid |
La próxima vez que abras una hoja de estilos para un layout nuevo, dibuja primero las cajas. Si trazas líneas que se cruzan, es Grid. Si cada caja va en una sola línea, es Flexbox.