Tres horas. Media hora para entender qué resuelve Grid que Flexbox no y por qué la mayoría de las adaptaciones no necesitan media query, y dos horas y media construyendo un catálogo que se adapta solo y rompiendo tu sitio a cinco anchuras.

Hoy · Hoja de ruta

  1. 1. Aprende: Qué problema bidimensional resuelve Grid, qué significa fr, cómo hacer que el número de columnas lo decida el espacio y dónde va de verdad un breakpoint.
  2. 2. Haz: Convierte el catálogo en cuadrícula, monta el esqueleto de página con áreas y recorre tu sitio a cinco anchuras corrigiendo lo que se rompa.
  3. 3. Comprueba: No hay scroll horizontal entre 320 px y 1600 px, y cada media query que hayas escrito responde a un problema que sabes nombrar.

Antes de empezar · 5 minutos, sin apuntes

  1. ¿Cuál es la diferencia entre margin y gap?
  2. ¿Qué hace flex-wrap?
  3. Decide si una botonera, una tarjeta horizontal y un catálogo necesitan Flexbox, Grid o ninguno.

Se explica

25 minutos · conceptos y demostración

La sesión tiene una sola idea de fondo: deja de decir cuántas columnas quieres y describe cuánto necesita cada una. Grid es la herramienta que lo permite, y el responsive es lo que sale de aplicarla bien.

Dos dimensiones a la vez

Flexbox coloca en una línea. Considera ahora este problema:

Producto  Producto  Producto
Producto  Producto  Producto
Producto  Producto  Producto

Aquí no hay una dirección: hay filas y columnas, y quieres que las columnas estén alineadas entre filas. Con Flexbox se puede aproximar, y las columnas nunca acaban de cuadrar porque cada fila reparte su espacio por su cuenta. Grid está diseñado exactamente para esto.

.catalogo {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 1.5rem;
}
Vista previaTres columnas alineadas y filas creadas automáticamente

Nova 14

Portátil ligero

Vision 27

Monitor IPS

Type Pro

Teclado mecánico

Dock One

Base USB-C

Wave

Ratón inalámbrico

Focus

Webcam 2K

Grid alinea cada tarjeta con las columnas de las demás filas; por eso es una herramienta bidimensional.

Tres declaraciones y tienes una cuadrícula de tres columnas con sus filas alineadas. Fíjate en que no has dicho cuántas filas hay: Grid las crea según los elementos que haya.

fr

Una fracción del espacio libre del contenedor. 1fr 1fr 1fr reparte lo disponible en tres partes iguales; 2fr 1fr da el doble a la primera columna.

Es distinta del porcentaje, y la diferencia importa: 33.33% no descuenta el gap, así que tres columnas al 33.33 % con separación desbordan. 1fr sí lo descuenta, porque reparte lo que queda después de los huecos. Por eso con Grid no tienes que hacer cuentas.

grid-template-columns: repeat(3, 1fr);   /* lo mismo, sin repetirte */
grid-template-columns: 250px 1fr;        /* una fija y otra elástica */
grid-template-columns: 1fr 2fr;          /* una parte y dos partes */

El vocabulario, y por qué se cuentan líneas

Las piezas de una cuadrícula
  1. Líneas · las divisiones, numeradas desde 1
  2. Pistas · las columnas y filas que quedan entre líneas
  3. Celdas · el cruce de una columna con una fila
  4. Áreas · un grupo rectangular de celdas, que puedes nombrar

Conviene fijarse en que se numeran las líneas, no las columnas. Una cuadrícula de tres columnas tiene cuatro líneas verticales: la 1 al principio y la 4 al final. Es la fuente de casi todos los desajustes de la sesión.

Usa siempre el inspector de Grid

En DevTools, junto a un elemento con display: grid, aparece una etiqueta grid. Púlsala y el navegador dibuja encima de la página las líneas con su numeración, las pistas y los huecos.

No intentes imaginar mentalmente una cuadrícula compleja si el navegador puede dibujártela. Cuando un elemento no cae donde esperabas, el inspector te enseña en un segundo qué línea es la 3 de verdad.

La línea más rentable de la unidad

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

Léela de dentro afuera:

minmax(16rem, 1fr)    cada columna mide como poco 16rem,
                      y crece para repartirse lo que sobre

auto-fit              caben las que quepan; el número
                      lo decide el espacio disponible

Es decir: deja de decir cuántas columnas quieres y describe cuánto necesita cada una. En un móvil cabrá una, en una tablet dos, en un monitor cuatro, y no has escrito una sola media query.

Áreas con nombre

Para el esqueleto de una página completa hay una forma que se lee sola:

.layout {
    display: grid;
    grid-template-columns: 15rem 1fr;
    grid-template-areas:
        "cabecera cabecera"
        "lateral  principal"
        "pie      pie";
    gap: 1rem;
}

header { grid-area: cabecera; }
aside  { grid-area: lateral; }
main   { grid-area: principal; }
footer { grid-area: pie; }

El dibujo entre comillas es el layout. Se ve de un vistazo que la cabecera ocupa las dos columnas y que el lateral está a la izquierda del contenido. Para modificarlo en una pantalla estrecha basta con redibujarlo:

@media (width < 48rem) {
    .layout {
        grid-template-columns: 1fr;
        grid-template-areas:
            "cabecera"
            "principal"
            "lateral"
            "pie";
    }
}

Fíjate en un detalle importante: en la pantalla estrecha el contenido va antes que el lateral, y el HTML no ha cambiado. Grid permite reordenar visualmente.

Reordenar visualmente tiene un límite

El orden del teclado sigue al HTML, no al CSS. Si mueves visualmente un bloque muy lejos de su sitio en el documento, quien navegue con Tab saltará de un lado a otro de la pantalla sin lógica aparente.

Reordenar el lateral y el contenido, que están contiguos, es inofensivo. Reordenar a lo grande no lo es. Si el orden visual y el del documento tienen que diferir mucho, lo que está mal es el orden del HTML.

Flexbox o Grid

No compiten. Una regla inicial que funciona:

Cómo elegir
  1. Una dimensión · Flexbox
  2. Filas y columnas a la vez · Grid

Una segunda pregunta afina la decisión: ¿quién debería decidir el tamaño, el contenedor o el contenido? Grid define la cuadrícula desde fuera y el contenido se acomoda; Flexbox parte de los elementos y reparte lo que sobra. Un menú cuyos enlaces miden cada uno lo suyo es Flexbox. Un catálogo cuyas tarjetas deben coincidir es Grid.

Lo más habitual en código real es que convivan:

GRID     para distribuir las tarjetas
  ↓
FLEXBOX  dentro de cada tarjeta

Primero fluido, después media queries

Quien entre a tu sitio puede hacerlo desde un móvil, una tablet, un portátil, un monitor grande, media pantalla en una ventana dividida, o un dispositivo que todavía no existe. No puedes enumerarlos.

El orden del responsive
  1. Primero, que el diseño se adapte solo
  2. Después, media queries solo donde el contenido ya no funcione

Llevas cuatro semanas construyendo herramientas que adaptan solas:

Herramienta Qué adapta sola
max-width y min() La anchura de los contenedores
flex-wrap El salto de línea cuando no caben
auto-fit con minmax() El número de columnas del catálogo
clamp() El tamaño de los titulares
gap La separación, sin cuentas

Si las usas bien, buena parte de tu sitio ya es responsive y no lo sabías. Las media queries son para lo que queda.

Mobile first

Escribir primero los estilos de la disposición más sencilla —una columna— y añadir después las variantes para pantallas anchas con min-width, en lugar de al revés.

/* Base: una columna. Vale para cualquier pantalla. */
.catalogo {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.5rem;
}

@media (width >= 48rem) {
    .catalogo { grid-template-columns: repeat(2, 1fr); }
}

@media (width >= 70rem) {
    .catalogo { grid-template-columns: repeat(3, 1fr); }
}
Vista previaUn mismo catálogo en tres espacios disponibles
Base · 1 columna
ABC
≥ 48rem · 2 columnas
ABCD
≥ 70rem · 3 columnas
ABCDEF

Mobile first parte de una columna y añade capacidad solo cuando el contenido dispone de espacio suficiente.

La razón no es ideológica: el caso de una columna es el más simple, y partir de lo simple para añadir complejidad da menos código que partir de lo complejo para irlo deshaciendo. La sintaxis (width >= 48rem) es la moderna y se lee sola; encontrarás también la clásica, (min-width: 48rem), que significa lo mismo.

Los breakpoints no son teléfonos

No busques las medidas del iPhone 15 ni del Galaxy de turno. Esa lista cambia cada año y nunca está completa.

Un breakpoint va donde tu contenido deja de funcionar: donde el menú ya no cabe, donde una columna se queda demasiado estrecha para leerse, donde la tarjeta se rompe. Esa anchura se encuentra estirando la ventana hasta que se ve mal, y es distinta en cada proyecto porque cada proyecto tiene otro contenido.

Ese es el motivo de que el paso 1 pida anotar a qué anchura empieza a verse mal el catálogo. Ese número vale más que cualquier lista de dispositivos.

Se trabaja

150 minutos · práctica sobre tu propio proyecto

El paso 1 provoca el problema a propósito y no lo arregla; el paso 2 lo resuelve con una sola línea. Los cuatro restantes llevan esa idea al sitio entero.

Paso 1 · Tu catálogo en cuadrícula · 20 min

En productos.html, convierte el contenedor de las fichas en un Grid.

  1. Aplica display: grid y gap al contenedor de los article.
  2. Prueba repeat(2, 1fr), después repeat(3, 1fr) y después repeat(4, 1fr). Quédate con la que mejor te encaje en tu pantalla.
  3. Abre el inspector de Grid y localiza las líneas, las pistas y los huecos.
  4. Ahora estrecha la ventana hasta 400 px y observa qué pasa con las tarjetas.

Todavía no lo arregles

Vas a ver columnas ridículamente estrechas, con una palabra por línea. Es lo esperado, y es el problema que resuelve el paso siguiente.

Anota qué has visto y a qué anchura ha empezado a verse mal. Ese número es tu primer breakpoint candidato, y lo has obtenido de la única forma legítima: mirando cuándo el contenido deja de funcionar.

Paso 2 · El catálogo que se adapta solo · 25 min

Aplica auto-fit con minmax() a tu página de productos. Comprueba de 320 px a 1600 px que en ningún momento hay columnas ilegibles ni tarjetas gigantes.

  1. Elige el mínimo de minmax() a partir del número que anotaste en el paso 1, no de una cifra redonda.
  2. Prueba también con auto-fill y explica en un comentario cuál has elegido y por qué.
  3. Comprueba con el inspector cuántas columnas hay a 320, 768, 1024 y 1600 px.
Consultar · auto-fit frente a auto-fill

Se parecen y hacen cosas distintas cuando sobra sitio:

Con pocos elementos y mucho espacio
auto-fit Las columnas vacías se colapsan, y las que hay se estiran para ocuparlo todo
auto-fill Se mantienen las columnas vacías, y los elementos conservan su tamaño

La forma de verlo es la experimentación: pon tres tarjetas en una pantalla ancha y cambia una palabra por la otra. Con auto-fit las tres se estiran; con auto-fill se quedan a la izquierda con su tamaño.

Paso 3 · El esqueleto de página con áreas · 30 min

Construye este layout con grid-template-areas:

┌───────────────────────────┐
│          HEADER           │
├─────────┬─────────────────┤
│ ASIDE   │      MAIN       │
│         │                 │
├─────────┴─────────────────┤
│          FOOTER           │
└───────────────────────────┘

y su versión en una columna para pantallas estrechas, con el contenido antes que el lateral. Decide tú a qué anchura cambia, y justifícalo por el contenido.

Consultar · colocar un elemento en celdas concretas

A veces un elemento debe ocupar más de una celda:

.destacado {
    grid-column: 1 / 3;    /* de la línea 1 a la 3: dos columnas */
}

.destacado {
    grid-column: span 2;   /* dos columnas, empiece donde empiece */
}

span suele ser más robusto: no depende de dónde acabe cayendo el elemento, cosa que cambia al variar el número de columnas.

Estoy atascado · un elemento no cae donde quiero

Abre el inspector de Grid y mira la numeración real de las líneas. El error casi siempre es contar columnas en lugar de líneas: para ocupar las dos primeras columnas hace falta 1 / 3, no 1 / 2.

Y si usas áreas, comprueba que todas las filas del dibujo tienen el mismo número de nombres. Si una fila tiene dos y otra tres, la plantilla entera es inválida y se ignora en silencio.

Paso 4 · ¿Flexbox, Grid, los dos o ninguno? · 15 min

Para cada interfaz, decide y justifica en una frase:

  1. La navegación principal.
  2. Una galería de imágenes en cuadrícula.
  3. Una botonera de dos botones.
  4. Un formulario de etiquetas y campos, uno debajo de otro.
  5. El catálogo de productos.
  6. Un panel con seis indicadores, uno de ellos del doble de ancho.
  7. La barra de precio y botón dentro de una tarjeta.
  8. El pie con tres columnas de enlaces.
Ver respuesta

1 · Flexbox. Una línea de enlaces que miden lo que miden.

2 · Grid. Filas y columnas que deben alinearse.

3 · Flexbox. Una dimensión y dos elementos.

4 · Ninguno. El flujo normal ya apila bloques; basta con márgenes o un gap si lo envuelves. No todo necesita un sistema de layout.

5 · Grid para la cuadrícula, y Flexbox dentro de cada tarjeta. Los dos.

6 · Grid. Además el indicador ancho se resuelve con span 2, que en Flexbox sería incómodo.

7 · Flexbox con space-between. Una dimensión.

8 · Grid si las tres columnas deben tener el mismo ancho; Flexbox si cada una puede medir lo suyo. Aquí las dos respuestas son defendibles, y lo que se evalúa es la justificación.

Paso 5 · Rompe tu página · 45 min

Es el trabajo central de la sesión. Con DevTools en modo dispositivo, recorre tu sitio a estas anchuras:

320 px    móvil pequeño
375 px    móvil habitual
768 px    tablet
1024 px   portátil
1440 px   escritorio

Busca a continuación, en las cuatro páginas:

Problema ¿Dónde aparece? A qué anchura Cómo lo has resuelto
Contenido que se sale
Scroll horizontal
Navegación que no cabe
Imágenes demasiado grandes
Columnas demasiado estrechas
Texto difícil de leer

Después corrígelos, intentando primero sin media query. Si lo resuelves con flex-wrap, con minmax() o con clamp(), mejor que con un breakpoint: no hay que mantenerlo.

Estoy atascado · tengo scroll horizontal y no sé de dónde sale

Es de los fallos más frustrantes, porque el culpable puede estar en cualquier sitio. Sospechosos por orden de frecuencia:

  1. Una imagen sin max-width: 100%.
  2. Un width fijo en píxeles mayor que la pantalla.
  3. Un elemento con 100% más un padding, sin box-sizing: border-box.
  4. Una tabla, o un bloque de código, que no cabe y no tiene su propio overflow-x: auto.
  5. Un margin negativo.

Para localizarlo rápido, en la consola de DevTools puedes recorrer los elementos y ver cuál es más ancho que el documento. O, de forma más rudimentaria: ve dando outline: 1px solid red a secciones enteras hasta ver cuál se sale.

Paso 6 · Defiende cada breakpoint · 15 min

Recorre todas las media queries de tu hoja y rellena una fila por cada una:

Anchura Qué deja de funcionar exactamente a esa anchura ¿Se podía resolver sin media query?

Elimina las que no puedas defender. Una media query que está ahí «por si acaso», o copiada de una lista de dispositivos, es código que habrá que mantener sin saber qué protege.

Ampliación si has completado el trabajo

Primero termina y comprueba los seis pasos. El segundo reto es el que más enseña de los dos, y consiste en borrar código.

Reto 1 · Un panel de indicadores

Construye un panel de seis indicadores con estas condiciones, sin una sola media query:

┌─────────────────┬────────┐
│   FACTURACIÓN   │ PEDIDOS│
│   (doble ancho) │        │
├────────┬────────┼────────┤
│ STOCK  │ DEVOL. │ VISITAS│
├────────┴────────┴────────┤
│        INCIDENCIAS        │
│      (todo el ancho)      │
└──────────────────────────┘
  1. Usa auto-fit con minmax() para las columnas, y span para los dos indicadores anchos.
  2. Comprueba qué ocurre a 320 px, donde solo cabe una columna. Un span 2 en una cuadrícula de una sola columna puede romper el layout: averigua qué pasa exactamente y resuélvelo.
  3. Consigue que los seis indicadores tengan la misma altura aunque su contenido sea de longitudes distintas. Di si eso lo resuelve Grid o Flexbox, y por qué.
  4. Anota en cuántas disposiciones distintas se reorganiza el panel entre 320 y 1600 px. No las has escrito: las decide el contenido.

Reto 2 · Quitar breakpoints

Esta hoja resuelve un catálogo y una cabecera con seis media queries. Todas funcionan, y la mayoría sobran.

.catalogo { display: grid; grid-template-columns: 1fr; gap: 16px; }
.tarjeta h3 { font-size: 18px; }
.cabecera { display: flex; justify-content: space-between; }
.contenedor { width: 100%; padding: 0 16px; }

@media (min-width: 480px) { .catalogo { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 768px) { .catalogo { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1024px){ .catalogo { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 768px) { .tarjeta h3 { font-size: 22px; } }
@media (min-width: 1280px){ .tarjeta h3 { font-size: 26px; } }
@media (min-width: 1280px){ .contenedor { width: 1240px; margin: 0 auto; padding: 0; } }
  1. Sustituye las tres primeras por una sola declaración sin media query.
  2. Sustituye las dos del titular por una sola declaración sin media query.
  3. Sustituye la del contenedor por una que no fije una anchura en píxeles.
  4. ¿Cuántas media queries quedan al terminar? Si queda alguna, defiéndela: di qué problema de contenido resuelve que no se pueda resolver de otro modo.
  5. Compara el número de líneas de las dos versiones y el número de valores que habría que revisar si mañana cambia el diseño.
Objetivo mínimoCatálogo adaptable sin media queries, esqueleto de página con áreas y las cinco anchuras recorridas sin scroll horizontal.
Si lo tienesLa tabla de problemas del paso 5 completa y cada breakpoint defendido en el paso 6.
RetoEl panel de indicadores funcionando sin media queries, y la hoja del reto 2 reducida con las eliminaciones justificadas.

Cierre

5 minutos · comprobación y recuerdo

Lista de verificación de la sesión

  • Tu catálogo se adapta sin media queries.
  • No hay scroll horizontal a 320 px en ninguna página.
  • Cada media query que has escrito responde a un problema concreto que puedes nombrar.
  • Ninguno de tus breakpoints procede de un modelo de teléfono.
  • Sabes justificar, para cada bloque, si es Flexbox o Grid.
  • El esqueleto de página usa áreas con nombre y se reordena en pantalla estrecha.

Antes de cerrar · 3 minutos, sin mirar

  1. ¿Por qué 1fr no es lo mismo que 33.33%?
  2. Una cuadrícula de cuatro columnas, ¿cuántas líneas verticales tiene?
  3. ¿Qué hace repeat(auto-fit, minmax(16rem, 1fr)), en una frase?
  4. ¿Qué riesgo tiene reordenar visualmente con Grid?
  5. ¿Qué significa mobile first y por qué se hace así?
  6. ¿Dónde debe ir un breakpoint?
Ver respuestas

1 · Porque fr reparte el espacio que queda después de descontar los gap, y el porcentaje no los descuenta.

2 · Cinco: una a cada lado de cada columna, contando los dos extremos.

3 · Crea tantas columnas como quepan, de al menos 16rem cada una, repartiéndose el espacio sobrante.

4 · Que el orden del teclado sigue al HTML, así que un reordenado grande hace que el foco salte por la pantalla sin lógica.

5 · Escribir primero la disposición más simple y añadir después las de pantallas anchas. Porque partir de lo simple y añadir sale más corto que partir de lo complejo y deshacer.

6 · Donde el contenido deja de funcionar, no donde está un modelo de dispositivo.

Microprueba semanal 4 · 5–10 minutos

Individual, sin IA y sin apuntes.

  1. Decide entre Flexbox, Grid o flujo normal para tres interfaces que no has visto.
  2. Escribe un Grid adaptable sin media query.
  3. Una página desborda a 360 px: enumera tres comprobaciones antes de añadir un breakpoint.