Flexbox va bien para una fila o una columna. Grid es la herramienta para dos ejes a la vez: filas y columnas que alinean, se solapan de forma controlada y reflowean sin un árbol de wrappers anidados.
La mayoría de bugs de Grid vienen de un modelo borroso de qué es un track, cómo reparte espacio libre fr y qué hace auto-fit cuando las columnas colapsan. Este post es ese modelo, más los patrones que pego en páginas reales.
El soporte en navegadores lleva años siendo sólido. Puedes tratar Grid como CSS base, no como un experimento de progressive enhancement.
El modelo mental en un párrafo
Una grid es un contenedor que define tracks (anchos de columna y altos de fila). Los items viven en celdas. Un item puede abarcar varios tracks. Colocas items por números de línea, por líneas con nombre o por áreas con nombre. El espacio libre se reparte con fr. El espacio libre acotado se reparte con minmax(). Las columnas responsivas que crecen y envuelven salen de repeat() con auto-fit o auto-fill.
Si ese párrafo se queda, el resto de Grid es vocabulario y unos pocos bordes afilados.
Tracks, líneas y celdas
Cuando escribes:
.grid {
display: grid;
grid-template-columns: 200px 1fr 1fr;
grid-template-rows: auto 1fr auto;
gap: 1rem;
}
creas:
- Tracks de columna: tres franjas verticales,
200px, luego dos partes iguales del ancho sobrante. - Tracks de fila: alto auto tipo cabecera, un medio flexible, auto tipo pie.
- Líneas: con tres columnas tienes cuatro líneas verticales (numeradas
1a4por defecto). Misma idea en filas. - Celdas: intersección de un track de columna y uno de fila.
- Gap: gutters entre tracks. Los gaps no son tracks. Los spans no se comen el gap como a veces parece el margin.
Los hijos fluyen a las celdas en orden del documento salvo que los coloques. La colocación es opcional. Una grid de cards simple no necesita reglas de placement.
Colocación por líneas (cuando la necesitas)
.hero {
grid-column: 1 / 3; /* linea de inicio 1, fin antes de la 3 */
grid-row: 1 / 2;
}
.sidebar {
grid-column: 3 / 4;
grid-row: 1 / 3;
}
grid-column: 1 / -1 es ancho completo: primera línea a última. Los índices negativos cuentan desde el final. Eso solo reemplaza muchos hacks de "full bleed dentro de un padre acotado".
Áreas con nombre: el mapa con el que conviene empezar
Para shells de página y dashboards, las áreas se leen mejor en review que los números de línea:
.page {
display: grid;
min-height: 100dvh;
grid-template-columns: 16rem 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"nav header"
"nav main"
"nav footer";
}
.page__nav { grid-area: nav; }
.page__header { grid-area: header; }
.page__main { grid-area: main; }
.page__footer { grid-area: footer; }
<div class="page">
<nav class="page__nav">...</nav>
<header class="page__header">...</header>
<main class="page__main">...</main>
<footer class="page__footer">...</footer>
</div>
Reglas para no liarla con áreas:
- Cada celda del mapa ASCII debe estar rellena. Usa
.para un hueco vacio si de verdad lo necesitas. - Una región con nombre debe ser un rectángulo. Las formas en L no valen.
- En pantallas pequeñas, cambia el mapa entero en una media query en vez de pelear placement item a item.
@media (max-width: 48rem) {
.page {
grid-template-columns: 1fr;
grid-template-rows: auto auto 1fr auto;
grid-template-areas:
"header"
"nav"
"main"
"footer";
}
}
El orden del documento puede seguir siendo lógico para accesibilidad. El orden visual cambia con el mapa de áreas.
fr: espacio libre, no "fracción del contenedor"
1fr significa una parte del espacio libre después de fijar tamaños fijos y min/max de contenido. No es siempre "un trozo igual del 100% del ancho".
grid-template-columns: 200px 1fr 2fr;
Si el contenedor mide 1000px de ancho y no hay gaps:
- Reserva
200pxpara la columna uno. - El espacio libre es
800px. - La columna dos se lleva
1/3del libre (~266px). - La columna tres se lleva
2/3del libre (~533px).
El contenido puede empujar un track mas ancho que su parte de fr cuando el min size del contenido supera la cuenta del espacio libre. Por eso a veces aparece overflow "de la nada". El arreglo suele ser un minimo explícito:
/* evita que un hijo ancho explote el track */
.grid > * {
min-width: 0;
}
/* o acota el track */
grid-template-columns: 200px minmax(0, 1fr) minmax(0, 2fr);
minmax(0, 1fr) es un hábito de producción para columnas fluidas que deben encoger por debajo del ancho intrínseco del contenido (tablas, URLs largas, bloques de código).
minmax(): suelos, techos y columnas honestas
minmax(min, max) define el rango permitido de un track. Grid resuelve el tamaño usado dentro de ese rango.
Patrones habituales:
/* sidebar que no se colapsa por debajo de lo legible ni se come la pagina */
grid-template-columns: minmax(12rem, 18rem) minmax(0, 1fr);
/* filas que crecen con el contenido pero se acotan para zonas con scroll */
grid-template-rows: auto minmax(0, 1fr) auto;
/* cards fluidas con un tamano preferido */
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
Piensa en minmax como la mesa de negociación entre la intención de diseño ("cards de unos 16rem") y la realidadd ("viewport de 340px" o "viewport de 1600px").
auto-fit vs auto-fill: la trampa de columnas responsivas
Ambos van con repeat() y un tamaño de track flexible, casi siempre minmax(...):
.cards {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}
La diferencia que importa:
| Keyword | Tracks vacíos |
|---|---|
auto-fill |
Mantiene columnas vacias como hueco al final |
auto-fit |
Colapsa tracks vacíos para que los items restantes estiren |
Con pocos items en una pantalla ancha:
auto-filldeja columnas en blanco y los items se quedan cerca de16rem(o su max) con huecos detras.auto-fitcolapsa esos vacíos, asi que la última fila de items crece para llenar la fila.
La mayoría de grids de marketing y dashboards de cards quieren auto-fit. Usa auto-fill cuando necesites huecos reservados o un ritmo fijo de columnas aunque algunas celdas esten vacias.
Ambos necesitan un espacio libre definido para contar cuántas columnas caben. El ancho del padre suele darlo. Grids anidadas dentro de padres shrink-wrapped pueden sorprenderte. Dale ancho a la grid (width: 100%, un track minmax(0, 1fr), etc.).
Tracks implícitos y empaquetado dense
Si colocas un item fuera de la plantilla explicita, Grid crea tracks implícitos. Por defecto:
grid-auto-rows: auto;
grid-auto-columns: auto;
grid-auto-flow: row; /* o column, o variantes dense */
grid-auto-flow: dense rellena huecos cuando los items tienen spans distintos. Util para muros de cards tipo masonry. Mas difícil de predecir para accesibilidad y orden de teclado, asi que prefírelo para galerías visuales, no para formularios.
.gallery {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 8rem;
grid-auto-flow: dense;
gap: 0.5rem;
}
.gallery__wide { grid-column: span 2; }
.gallery__tall { grid-row: span 2; }
Alineación: dos capas que la gente mezcla
Grid alinea tracks dentro del contenedor e items dentro de su área:
.grid {
justify-content: center; /* tracks como grupo en el eje inline */
align-content: start; /* tracks como grupo en el eje block */
justify-items: stretch; /* por defecto: el item llena el ancho de la celda */
align-items: stretch; /* por defecto: el item llena el alto de la celda */
}
.item {
justify-self: end;
align-self: center;
}
Por defecto los items se estiran. Por eso un botón dentro de una celda parece a ancho completo hasta que pones justify-items: start o justify-self: start en el item.
place-items, place-content y place-self son los atajos de dos ejes.
Layouts comunes que puedes enviar a producción
1. Grid de cards responsiva
.cards {
display: grid;
gap: 1.25rem;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
}
min(100%, 18rem) evita overflow horizontal en viewports mas estrechos que 18rem.
2. Holy grail / shell de app
.shell {
display: grid;
min-height: 100dvh;
grid-template-columns: clamp(12rem, 20vw, 18rem) minmax(0, 1fr);
grid-template-rows: auto 1fr auto;
grid-template-areas:
"side head"
"side main"
"side foot";
}
3. Formulario con labels en columna limpia
.form {
display: grid;
gap: 0.75rem 1rem;
grid-template-columns: max-content minmax(0, 1fr);
align-items: center;
}
.form .full {
grid-column: 1 / -1;
}
4. Media object sin gimnasia de Flex
.media {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 1rem;
align-items: start;
}
5. Grid de producto de doce columnas (cuando diseño te la pasa)
.product {
display: grid;
grid-template-columns: repeat(12, minmax(0, 1fr));
gap: 1rem;
}
.product__hero { grid-column: span 8; }
.product__aside { grid-column: span 4; }
.product__full { grid-column: 1 / -1; }
En un breakpoint, baja a repeat(6, ...) o una sola columna y resetea los spans. Las áreas con nombre suelen ganar a doce columnas para shells; doce columnas siguen ayudando cuando el design system habla en "span 8 / span 4".
Grid vs Flex: una regla de decision simple
| Situación | Prefiere |
|---|---|
| Fila o columna unidimensional de controles | Flex |
| Filas de cards de igual alto que deben alinear en dos dimensiones | Grid |
| Regiones de página (nav, main, aside) | Áreas de Grid |
| Lista de chips con wrap e indeterminada | Flex wrap o Grid auto-fit |
| Centrar una caja en el viewport | Cualquiera; Grid place-items: center es corto |
| Toolbars de un eje dentro de una página de dos ejes | Shell Grid + toolbars Flex |
Usarás ambos en la misma página. Grid para estructura, Flex para micro-layout dentro de las celdas.
Checklist de depuración
Cuando un layout "se niega" a encoger o alinear:
- El item es un grid item, o esta anidado un nivel más de lo que crees?
- Un hijo necesita
min-width: 0/min-height: 0? - Estas luchando contra el
stretchpor defecto? - Un
1frchoco con un min content intrínseco mayor que el espacio libre? - El ancho del padre es indefinido y
auto-fitno puede contar columnas? - Abre el overlay de Grid en DevTools. Ahi salen líneas y nombres de área. Fíate del overlay más que de la intuición.
Que memorizar
- Los tracks definen el esqueleto. Los items llenan celdas o spans.
- Las áreas son el mapa legible del chrome de página.
frreparte el espacio libre tras tamaños fijos y mínimos.minmax(0, 1fr)es la columna fluida que de verdad se encoge.auto-fitcolapsa tracks vacíos;auto-filllos mantiene.- Grid para dos ejes, Flex para uno. Anídalos sin miedo.
Cuando esos seis puntos sean memoria muscular, Grid deja de sentirse como una sintaxis especial y empieza a sentirse como dibujar una tabla que sabe reflowear.
