Layout avanzado
Lección 35 de 37 · 5 min
Grid ya resuelve casi todo. Estas cuatro piezas cubren lo que queda: dibujar el layout con nombres, alinear el interior de tarjetas vecinas, fijar proporciones y apilar elementos de alto distinto sin huecos.
Áreas con nombre
grid-template-areas dibuja la grilla con texto. Cada cadena entre comillas es una fila y cada palabra, una celda. Si repetís un nombre en celdas vecinas, esa área ocupa todas; un punto (.) deja una celda vacía. Después, cada hijo elige su área con grid-area.
.receta {display: grid;grid-template-columns: 1fr 2fr;grid-template-areas:"titulo titulo""ingredientes pasos""notas pasos";}.titulo { grid-area: titulo; }.pasos { grid-area: pasos; }
Cada área tiene que ser un rectángulo. Una forma de L o un nombre repetido en celdas separadas hace inválida toda la declaración, y la grilla queda sin áreas. Probá las plantillas: el HTML es siempre el mismo y solo cambian las cadenas.
Receta de locro acomodada en una grilla de 2 columnas y 3 filas, con cada área marcada con su nombre
Locro criollo
4 h · 8 porciones
Ingredientes
- 500 g de maíz blanco partido
- 250 g de porotos blancos
- 500 g de zapallo
- 300 g de falda
- 2 chorizos colorados
Preparación
- Remojá el maíz y los porotos desde la noche anterior.
- Hervilos 2 horas con la falda cortada en cubos.
- Sumá el zapallo y los chorizos, y cociná a fuego bajo hasta que espese.
Para servir
Con salsa de pimentón, aceite y cebolla de verdeo.
.receta {display: grid;grid-template-columns: 1fr 2fr;grid-template-areas:"titulo titulo""ingredientes pasos""notas pasos";gap: 12px;}.titulo { grid-area: titulo; }.ingredientes { grid-area: ingredientes; }.pasos { grid-area: pasos; }.notas { grid-area: notas; }
<article class="grid grid-cols-[1fr_2fr] [grid-template-areas:'titulo_titulo'_'ingredientes_pasos'_'notas_pasos'] gap-3"><header class="[grid-area:titulo]">Locro criollo</header><ul class="[grid-area:ingredientes]">…</ul><ol class="[grid-area:pasos]">…</ol><p class="[grid-area:notas]">Con salsa de pimentón, aceite y cebolla de verdeo.</p></article>
Por eso las áreas sirven tanto para el diseño adaptable: en cada media query (o container query) redibujás el mapa y no tocás nada más. Los hijos siguen con su grid-area.
.receta {grid-template-areas: "titulo" "ingredientes" "pasos" "notas";}@media (width >= 40rem) {.receta {grid-template-columns: 1fr 2fr;grid-template-areas:"titulo titulo""ingredientes pasos""notas pasos";}}
Subgrid
Una fila de tarjetas tiene un problema conocido: si un título ocupa dos líneas, todo lo que viene abajo en esa tarjeta baja, y las tarjetas vecinas quedan desparejas. Cada tarjeta es su propia grilla y no sabe nada de las otras.
Con subgrid, la tarjeta no define sus filas: usa las de la grilla madre. Para eso ocupa varias filas de la madre con grid-row: span 3 y declara grid-template-rows: subgrid. Ahora la fila del título mide lo que mide el título más largo, en todas las tarjetas.
La lista usa repeat(auto-fit, minmax(8.5rem, 1fr)): entran tantas columnas de al menos 8.5rem como quepan, y se reparten el sobrante. En un celular quedan dos columnas y la tercera tarjeta baja; subgrid alinea igual las tarjetas de cada banda.
Etiopía Guji
Durazno, jazmín y té negro. Lavado.
250 g · $ 16.800
Colombia Huila, Finca El Paraíso
Frutilla y cacao. Fermentación anaeróbica de 72 horas.
250 g · $ 21.500
Brasil Cerrado
Avellana y dulce de leche.
250 g · $ 12.900
.cafes {display: grid;grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));gap: 12px;}.cafe {grid-row: span 3; /* ocupa las 3 filas: nombre, notas y precio */display: grid;grid-template-rows: subgrid;row-gap: 6px;}
<ul class="grid grid-cols-[repeat(auto-fit,minmax(8.5rem,1fr))] gap-3"><li class="row-span-3 grid grid-rows-subgrid gap-y-1.5"><h3>Etiopía Guji</h3><p>Durazno, jazmín y té negro. Lavado.</p><p>250 g · $ 16.800</p></li>…</ul>
Las líneas naranjas marcan dónde empiezan las notas y el precio. Sin subgrid, los precios igual quedan alineados abajo (la fila del medio tiene 1fr y absorbe la diferencia), pero las notas arrancan a distinta altura. La subgrilla hereda el gap de la madre, salvo que declares el suyo, como hace acá row-gap: 6px.
Funciona igual con columnas (grid-template-columns: subgrid), por ejemplo para alinear etiquetas y campos de un formulario. Subgrid es Baseline ampliamente disponible desde marzo de 2026.
aspect-ratio
aspect-ratio fija la proporción entre ancho y alto. Le das un ancho y el navegador calcula el alto: con 16 / 9 y 256px de ancho, el alto es 144px. Reemplaza al viejo truco de padding-top: 56.25%.
Foto de portada con aspect-ratio 16 / 9: 256 de ancho por 144 de alto, en píxeles

Crónica · Santa Cruz
Por qué el hielo de los glaciares se ve celeste
.portada {width: 256px;max-width: 100%;}.foto {aspect-ratio: 16 / 9;width: 100%;object-fit: cover;}
<article class="w-64 max-w-full"><img class="aspect-video w-full object-cover" src="glaciar.webp" alt="Bloques de hielo de un glaciar">…</article>
Si la foto no tiene esa proporción, object-fit: cover la recorta para llenar el marco sin deformarla. Y si el contenido no entra en la caja, la caja crece: la proporción es una preferencia, no un límite. Con min-height: 0 o overflow: hidden se mantiene fija.
En un <img> con los atributos width y height, el navegador ya usa esa proporción mientras la imagen carga, así la página no salta.aspect-ratio es Baseline ampliamente disponible.
Masonry: grid-lanes
Masonry es el layout de muro: columnas parejas y elementos de alto distinto, cada uno en la columna que tenga más espacio libre, sin los huecos que deja una grilla. Se discutió años con el nombre de masonry; la sintaxis que quedó es display: grid-lanes, del nivel 3 de Grid.
.notas {display: grid-lanes;grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));gap: 12px;}
Definís las columnas como en grid y el navegador llena los carriles (lanes). Si definís filas en lugar de columnas, el muro es horizontal. La propiedad flow-tolerance decide cuánta diferencia de altura se ignora antes de saltar a otra columna, para que el orden no quede demasiado mezclado.
Todavía no es Baseline: hoy solo lo soporta Safari 26.4. Mientras tanto, el respaldo clásico son las columnas de texto: columns con break-inside: avoid. El resultado se parece, pero el orden va de arriba abajo por columna. Mirá los números naranjas en cada modo.
Tu navegador todavía no soporta grid-lanes, así que en esa opción ves el respaldo con columnas.
Notas de campo
Reserva Costanera Sur · sábado
- 07:05Entramos por Brasil. Niebla sobre la laguna de los Coipos.
- 07:20Hornero.
- 07:42Pareja de macáes cerca de los juncos. Contamos cuatro pichones arriba del lomo de la hembra.
- 08:10Garza blanca.
- 08:25Bandada de cotorras sobre los sauces, muy ruidosa.
- 08:50Biguá secándose las alas en un tronco caído.
- 09:15Picaflor común en las flores del ceibo. Se quedó casi un minuto.
- 09:40Salida. 23 especies.
/* respaldo para todos: columnas de texto */.notas {columns: 8rem;column-gap: 12px;}.nota {break-inside: avoid;margin-block-end: 12px;}@supports (display: grid-lanes) {.notas {display: grid-lanes;grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));gap: 12px;columns: auto;}.nota {margin-block-end: 0;}}
<ol class="columns-[8rem] gap-3 supports-[display:grid-lanes]:[display:grid-lanes] supports-[display:grid-lanes]:grid-cols-[repeat(auto-fill,minmax(8rem,1fr))] supports-[display:grid-lanes]:columns-auto"><li class="break-inside-avoid mb-3 supports-[display:grid-lanes]:mb-0">Entramos por Brasil. Niebla sobre la laguna de los Coipos.</li>…</ol>
Con @supports (display: grid-lanes) el mismo CSS sirve en los dos mundos: todos reciben las columnas, y los navegadores que entienden grid-lanes lo pisan. En grid común, cada fila mide lo que mide su nota más alta y quedan los huecos que masonry evita.
En Tailwind
Tailwind tiene clases para subgrid y para las proporciones comunes. Las áreas y grid-lanes no tienen clase propia: van como propiedades arbitrarias entre corchetes, con guiones bajos en lugar de espacios.
| CSS | Tailwind |
|---|---|
| grid-template-areas | [grid-template-areas:'titulo_titulo'_…] |
| grid-area: pasos | [grid-area:pasos] |
| grid-template-rows: subgrid | grid-rows-subgrid |
| grid-row: span 3 | row-span-3 |
| repeat(auto-fit, minmax(8.5rem, 1fr)) | grid-cols-[repeat(auto-fit,minmax(8.5rem,1fr))] |
| aspect-ratio: 16 / 9 | aspect-video |
| aspect-ratio: 1 | aspect-square |
| display: grid-lanes | [display:grid-lanes] |
<ul class="grid grid-cols-[repeat(auto-fit,minmax(8.5rem,1fr))] gap-3"><li class="row-span-3 grid grid-rows-subgrid gap-y-1.5"><h3>Etiopía Guji</h3><p>Durazno, jazmín y té negro.</p><p>250 g · $ 16.800</p></li></ul><ol class="columns-[8rem] gap-3supports-[display:grid-lanes]:[display:grid-lanes]supports-[display:grid-lanes]:grid-cols-[repeat(auto-fill,minmax(8rem,1fr))]supports-[display:grid-lanes]:columns-auto"><li class="mb-3 break-inside-avoidsupports-[display:grid-lanes]:mb-0">…</li></ol>
Advanced layout
Lesson 35 of 37 · 4 min
Grid already handles most layouts. These four pieces cover the rest: drawing the layout with names, lining up the insides of neighbouring cards, locking proportions and stacking items of different heights without gaps.
Named areas
grid-template-areas draws the grid as text. Each quoted string is a row and each word a cell. Repeat a name in neighbouring cells and that area spans them; a dot (.) leaves a cell empty. Then each child picks its area with grid-area.
.recipe {display: grid;grid-template-columns: 1fr 2fr;grid-template-areas:"header header""ingredients steps""notes steps";}.header { grid-area: header; }.steps { grid-area: steps; }
Every area must be a rectangle. An L shape, or a name repeated in cells that don't touch, makes the whole declaration invalid and the grid ends up with no areas. Try the templates: the HTML never changes, only the strings do.
A locro recipe laid out on a grid of 2 columns and 3 rows, each area tagged with its name
Criollo locro
4 h · serves 8
Ingredients
- 500 g cracked white corn
- 250 g white beans
- 500 g squash
- 300 g beef flank
- 2 chorizo sausages
Method
- Soak the corn and beans overnight.
- Simmer them for 2 hours with the flank, diced.
- Add the squash and chorizo, and cook on low heat until thick.
To serve
With a sauce of paprika, oil and spring onion.
.recipe {display: grid;grid-template-columns: 1fr 2fr;grid-template-areas:"header header""ingredients steps""notes steps";gap: 12px;}.header { grid-area: header; }.ingredients { grid-area: ingredients; }.steps { grid-area: steps; }.notes { grid-area: notes; }
<article class="grid grid-cols-[1fr_2fr] [grid-template-areas:'header_header'_'ingredients_steps'_'notes_steps'] gap-3"><header class="[grid-area:header]">Criollo locro</header><ul class="[grid-area:ingredients]">…</ul><ol class="[grid-area:steps]">…</ol><p class="[grid-area:notes]">With a sauce of paprika, oil and spring onion.</p></article>
That's why areas suit responsive design so well: each media query (or container query) redraws the map and nothing else. The children keep their grid-area.
.recipe {grid-template-areas: "header" "ingredients" "steps" "notes";}@media (width >= 40rem) {.recipe {grid-template-columns: 1fr 2fr;grid-template-areas:"header header""ingredients steps""notes steps";}}
Subgrid
A row of cards has a well-known problem: when one title wraps to two lines, everything below it in that card moves down, and the cards next to it stop lining up. Each card is its own grid and knows nothing about the others.
With subgrid, the card doesn't define its rows: it borrows the parent's. It spans several parent rows with grid-row: span 3 and declares grid-template-rows: subgrid. Now the title row is as tall as the longest title, in every card.
The list uses repeat(auto-fit, minmax(8.5rem, 1fr)): as many columns of at least 8.5rem as fit, sharing whatever is left. On a phone that's two columns and the third card wraps; subgrid still lines up the cards within each band.
Ethiopia Guji
Peach, jasmine and black tea. Washed.
250 g · $16.80
Colombia Huila, Finca El Paraíso
Strawberry and cocoa. 72-hour anaerobic fermentation.
250 g · $21.50
Brazil Cerrado
Hazelnut and caramel.
250 g · $12.90
.coffees {display: grid;grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));gap: 12px;}.coffee {grid-row: span 3; /* takes all 3 rows: name, notes and price */display: grid;grid-template-rows: subgrid;row-gap: 6px;}
<ul class="grid grid-cols-[repeat(auto-fit,minmax(8.5rem,1fr))] gap-3"><li class="row-span-3 grid grid-rows-subgrid gap-y-1.5"><h3>Ethiopia Guji</h3><p>Peach, jasmine and black tea. Washed.</p><p>250 g · $16.80</p></li>…</ul>
The orange lines mark where the notes and the price start. Without subgrid the prices still line up at the bottom (the middle row is 1fr and absorbs the difference), but the notes start at different heights. The subgrid inherits the parent's gap unless it sets its own, as row-gap: 6px does here.
It works the same with columns (grid-template-columns: subgrid), for example to line up a form's labels and fields. Subgrid has been Baseline widely available since March 2026.
aspect-ratio
aspect-ratio sets the ratio between width and height. Give the box a width and the browser works out the height: at 16 / 9 and 256px wide, it's 144px tall. It replaces the old padding-top: 56.25% trick.
Lead photo with aspect-ratio 16 / 9: 256 wide by 144 tall, in pixels

Feature · Patagonia
Why glacier ice looks blue
.cover {width: 256px;max-width: 100%;}.photo {aspect-ratio: 16 / 9;width: 100%;object-fit: cover;}
<article class="w-64 max-w-full"><img class="aspect-video w-full object-cover" src="glaciar.webp" alt="Blocks of glacier ice">…</article>
When the photo has a different ratio, object-fit: cover crops it to fill the frame without stretching it. And when the content doesn't fit, the box grows: the ratio is a preference, not a limit. min-height: 0 or overflow: hidden keeps it fixed.
On an <img> with width and height attributes, the browser already uses that ratio while the image loads, so the page doesn't jump. aspect-ratio is Baseline widely available.
Masonry: grid-lanes
Masonry is the wall layout: even columns, items of different heights, each placed in the column with the most room, without the holes a grid leaves. It was debated for years under the name masonry; the syntax that stuck is display: grid-lanes, from Grid Level 3.
.notes {display: grid-lanes;grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));gap: 12px;}
You define the columns as in grid and the browser fills the lanes. Define rows instead and the wall runs sideways. The flow-tolerance property sets how much height difference is ignored before an item jumps to another column, so the order doesn't get too scrambled.
It's not Baseline yet: only Safari 26.4 supports it today. Until then, the classic fallback is text columns: columns with break-inside: avoid. It looks similar, but the order runs top to bottom down each column. Watch the orange numbers in each mode.
Your browser doesn't support grid-lanes yet, so that option shows the column fallback.
Field notes
Costanera Sur reserve · Saturday
- 07:05In through the Brasil gate. Mist over the Coipos lagoon.
- 07:20Rufous hornero.
- 07:42A pair of great grebes by the reeds. We counted four chicks riding on the female's back.
- 08:10Great egret.
- 08:25A flock of monk parakeets over the willows, very loud.
- 08:50Cormorant drying its wings on a fallen trunk.
- 09:15Glittering-bellied emerald at the ceibo flowers. Stayed almost a minute.
- 09:40Out. 23 species.
/* fallback for everyone: text columns */.notes {columns: 8rem;column-gap: 12px;}.note {break-inside: avoid;margin-block-end: 12px;}@supports (display: grid-lanes) {.notes {display: grid-lanes;grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));gap: 12px;columns: auto;}.note {margin-block-end: 0;}}
<ol class="columns-[8rem] gap-3 supports-[display:grid-lanes]:[display:grid-lanes] supports-[display:grid-lanes]:grid-cols-[repeat(auto-fill,minmax(8rem,1fr))] supports-[display:grid-lanes]:columns-auto"><li class="break-inside-avoid mb-3 supports-[display:grid-lanes]:mb-0">In through the Brasil gate. Mist over the Coipos lagoon.</li>…</ol>
With @supports (display: grid-lanes) the same CSS works in both worlds: everyone gets the columns, and browsers that understand grid-lanes override them. In a plain grid, each row is as tall as its tallest note, which leaves the holes masonry avoids.
In Tailwind
Tailwind has classes for subgrid and for common ratios. Areas and grid-lanes have no class of their own: they go in brackets as arbitrary properties, with underscores instead of spaces.
| CSS | Tailwind |
|---|---|
| grid-template-areas | [grid-template-areas:'header_header'_…] |
| grid-area: steps | [grid-area:steps] |
| grid-template-rows: subgrid | grid-rows-subgrid |
| grid-row: span 3 | row-span-3 |
| repeat(auto-fit, minmax(8.5rem, 1fr)) | grid-cols-[repeat(auto-fit,minmax(8.5rem,1fr))] |
| aspect-ratio: 16 / 9 | aspect-video |
| aspect-ratio: 1 | aspect-square |
| display: grid-lanes | [display:grid-lanes] |
<ul class="grid grid-cols-[repeat(auto-fit,minmax(8.5rem,1fr))] gap-3"><li class="row-span-3 grid grid-rows-subgrid gap-y-1.5"><h3>Ethiopia Guji</h3><p>Peach, jasmine and black tea.</p><p>250 g · $16.80</p></li></ul><ol class="columns-[8rem] gap-3supports-[display:grid-lanes]:[display:grid-lanes]supports-[display:grid-lanes]:grid-cols-[repeat(auto-fill,minmax(8rem,1fr))]supports-[display:grid-lanes]:columns-auto"><li class="mb-3 break-inside-avoidsupports-[display:grid-lanes]:mb-0">…</li></ol>