Ir al contenido
wannacode

Diseño responsive

En resumen: El diseño responsive hace que una página funcione en cualquier pantalla: la etiqueta meta viewport, mobile first y las media queries que cambian la disposición según el ancho.

Problema

El botón está fuera de la pantalla

La página del producto se hizo en un monitor, y allí se ve bien. Un cliente con un teléfono escribe: «No puedo pedir, no hay ningún botón». Aquí está con el ancho de un teléfono, 360px:

HTML, CSS, JS

El botón «Pedir» está a la derecha, fuera de la pantalla. La página se desplaza hacia los lados, y a la mayoría no se le ocurre desplazarla. El código no tiene ningún error: la imagen mide 400px, la columna de texto 360px, y van una al lado de la otra. En un monitor de 1000px de ancho cabe. En 360px no.

La idea

Las pantallas tienen anchos distintos

La misma página se abre en un teléfono (360–430px), en una tableta (unos 800px) y en una computadora (1000px o más). Una página que se adapta a cualquier ancho se llama responsive.

El primer paso está en <head>:

<meta name="viewport" content="width=device-width, initial-scale=1">

Sin ella, el teléfono se hace pasar por un monitor: coloca la página a 980px y la reduce hasta que cabe. El texto queda diminuto, y tu CSS nunca ve los 360px reales. Con ella, el ancho de la página es el ancho de la pantalla. En el sandbox añadimos esta línea por ti, pero en tus propias páginas la escribes tú.

El segundo paso es el orden en que escribes el CSS. Mobile first significa: las reglas base son para la pantalla más estrecha. Allí todo va uno debajo de otro, y eso casi siempre cabe. Luego añades cambios para las pantallas más anchas con una media query:

.product {
display: grid;
gap: 16px;
}
@media (min-width: 700px) {
.product {
grid-template-columns: 1fr 1fr;
}
}

@media (min-width: 700px) es una condición: «el ancho es de al menos 700px». Las reglas de dentro solo se aplican si la condición es verdadera. Si no, el navegador se las salta.

El tercer paso son las imágenes. width="400" es bueno para el HTML: el navegador reserva el espacio mientras carga la imagen. Pero en una pantalla estrecha necesita un techo:

img {
max-width: 100%;
height: auto;
}

max-width: 100% significa «no más ancha que su caja», y height: auto conserva las proporciones para que no se deforme.

Míralo

Mira cómo se activan las consultas

La ventana crece de 360px a 1000px. Fíjate en la escala de arriba y en las líneas del código.

body { margin: 0; padding: 16px; }
.cards {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
@media (min-width: 600px) {
.cards { grid-template-columns: 1fr 1fr; }
}
@media (min-width: 900px) {
.cards { grid-template-columns: repeat(4, 1fr); }
}
Paso a paso, en palabras
  1. Un teléfono, 360px. La regla base es para la pantalla más estrecha: una columna. Una tarjeta mide 360 − 2 × 16 = 328px.
  2. La ventana se ensancha y la columna crece con ella. A 599px la condición min-width: 600px todavía es falsa y el navegador se salta las reglas de dentro.
  3. A 600px la condición es verdadera. Su regla va después de la base y gana: dos columnas de 276px. Las tarjetas se reordenan.
  4. Aún más ancho: las dos columnas crecen hasta 425,5px. min-width: 900px todavía es falsa.
  5. A 900px las dos consultas son verdaderas. Fijan la misma propiedad, así que gana la última: cuatro columnas de 205px.
  6. A 1000px no se activa nada nuevo. Las cuatro columnas crecen hasta 230px.
  7. El navegador vuelve a comprobar las consultas con cada cambio de ancho: un teléfono girado, una ventana más estrecha. Si la estrechas, las columnas vuelven por el mismo camino.

Una media query no se calcula una sola vez. El navegador la comprueba con cada cambio de ancho y recoloca la página al instante. Las dos consultas fijan la misma propiedad con la misma especificidad, así que a 900px gana la última. Es la cascada que ya conoces.

Pruébalo

Arréglala para un teléfono

La página mide 360px de ancho. Hazla mobile first: la imagen y el texto uno debajo de otro, y nada que sobresalga por los lados. La imagen no es más ancha que su caja. A partir de 700px, la imagen y el texto van en dos columnas. La comprobación busca el desplazamiento lateral y en cuántas filas están la imagen y la columna de texto.

HTML, CSS, JS

Pruébalo

Ahora para una computadora

Esta página ya está hecha para un teléfono: todo en una columna. Aquí el ancho es de 1000px. Añade una media query para que, a partir de 700px, la imagen y el texto vayan en dos columnas iguales.

HTML, CSS, JS

Pruébalo

Encuentra el error

En una computadora debería haber dos columnas, pero la página se queda en una. ¿Por qué?

Elige la línea con el error.

Pruébalo

Completa

¿Qué palabra hace que las dos columnas se apliquen a partir de 700px?

@media (: 700px) {
  .product {
    grid-template-columns: 1fr 1fr;
  }
}

La idea

El color en un solo lugar

El verde de la tienda está en el CSS en cuatro sitios: el precio, los enlaces, el botón y el borde. La tienda cambia de color y alguien tiene que encontrarlos todos. Si se le escapa uno, la página queda con dos colores.

Una variable CSS (propiedad personalizada) es un valor con nombre. El nombre empieza con dos guiones, y var() lo lee:

:root {
--accent: #1a7f37;
}
button {
background: var(--accent);
}

:root es el elemento más alto, <html>. Las variables se heredan como color, así que lo que está en :root se ve en toda la página. El color cambia en una sola línea.

Lo mismo da un tema oscuro. @media (prefers-color-scheme: dark) es verdadera cuando alguien ha elegido un tema oscuro en su teléfono o computadora. Dentro solo cambias los valores de las variables. Las reglas que las usan siguen igual.

Míralo

Claro y oscuro

HTML, CSS, JS

El sandbox sigue el tema de tu navegador, no el botón de tema de esta web. Cambia el tema de tu sistema a oscuro y pulsa «Ejecutar»: el fondo, el texto y el verde cambian, mientras que las reglas CSS de .card, .price y button son las mismas. El verde sobre oscuro es más claro, para que se lea sobre el fondo oscuro.

Pruébalo

Un color nuevo para la tienda

La tienda cambia el verde #1a7f37 por el naranja #c2410c. Crea una variable --accent en :root con el valor #c2410c y úsala en los cuatro sitios donde ahora está el verde.

HTML, CSS, JS

La idea

Estados: señalado, enfocado, desactivado

Un botón no se ve de una sola manera. Puede estar bajo el ratón, enfocado con el teclado o desactivado. CSS los atrapa con pseudoclases: una palabra con dos puntos detrás del selector.

button:hover {
background: #116329;
}
button:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 2px;
}
button:disabled {
background: #9ca3af;
cursor: not-allowed;
}
  • :hover: el ratón está sobre el elemento. Un teléfono no tiene ratón, así que no se esconde información importante tras :hover.
  • :focus-visible: el elemento tiene el foco y el navegador decide que el foco debe verse. Con el teclado siempre se ve. Con un clic de ratón en un botón, normalmente no. Por eso outline: none en :focus es un error: quien usa el teclado pierde de vista dónde está.
  • :disabled: el elemento tiene el atributo disabled. El botón «Agotado» debe verse desactivado, no solo estarlo.

Y el tamaño. En un teléfono se pulsa con el dedo, que es menos preciso que un ratón. Un botón de 24px de alto se falla. Da a los botones una altura de al menos 44px: min-height: 44px.

Pruébalo

Los botones en un teléfono

Un compañero quitó el contorno del foco, y los botones son bajos. Haz que los botones tengan al menos 44px de alto (min-height); que con el foco del teclado haya un contorno continuo de 3px del color --accent; y que el botón desactivado «Agotado» sea gris (#9ca3af), con el cursor not-allowed. La comprobación pulsa Tab y mira el botón «Pedir».

HTML, CSS, JS
Más precisamente, para curiosos

El viewport, con más precisión. Sin la etiqueta meta, los navegadores móviles colocan la página a 980px y la reducen. En Chromium con un teléfono emulado de 360px, innerWidth es 980 y @media (min-width: 700px) es verdadera: el teléfono recibe la disposición de la computadora. initial-scale=1 significa «sin reducir al empezar». No desactives el zoom (user-scalable=no, maximum-scale=1): las personas con poca vista agrandan la página con dos dedos.

Puntos de ruptura (breakpoints). 600, 700, 900 no son números sagrados. Pon un punto de ruptura donde tu diseño empiece a verse mal, no en el modelo de algún teléfono. Algunos escriben las consultas en em (min-width: 40em): así también siguen el tamaño de letra que la persona haya elegido.

Consultas no solo de ancho. (hover: hover) es verdadera cuando el dispositivo señalador principal puede pasar por encima (un ratón), y (pointer: coarse) lo es para un dedo. (prefers-reduced-motion: reduce) dice que la persona no quiere movimiento: quita las animaciones. (orientation: landscape) es para una pantalla en horizontal.

Las variables, con más precisión. var(--accent, #1a7f37) tiene un valor de reserva por si falta la variable. Una variable puede cambiar en una parte de la página: .sale { --accent: #c2410c; } y todo lo de .sale es naranja. El valor se comprueba solo cuando se usa: --accent: 12px está permitido, pero entonces color: var(--accent) no es válido y el color pasa a ser el heredado. La propiedad color-scheme: light dark le dice al navegador que pinte también sus propias partes (campos, barras de desplazamiento) con el tema oscuro.

Tamaño para el dedo. WCAG 2.2 (criterio 2.5.8) pide un objetivo de al menos 24 × 24px o suficiente espacio alrededor. Apple recomienda 44 × 44pt y Material Design 48 × 48dp.

Ejercicio

El catálogo en un teléfono

El catálogo se hizo para una computadora: cuatro tarjetas de 220px en una fila. Aquí el ancho es de 360px. Hazlo para un teléfono: las tarjetas en dos columnas, sin desplazamiento lateral, imágenes no más anchas que su tarjeta, los botones «Comprar» de al menos 44px de alto (min-height). Pon el verde #1a7f37 en una variable --accent en :root y haz que los botones la usen. Si quieres, añade también cuatro columnas a partir de 700px.

HTML, CSS, JS

Test

Ponte a prueba

1. Escribes mobile first. ¿Qué regla no tiene media query?
2. ¿Qué pasa con @media (min-width: 600px) en una ventana de 599px y en una de 600px?
3. :root tiene --accent: #c2410c, y el botón tiene background: var(accent). ¿De qué color es el botón?
4. ¿Por qué :focus-visible y no :hover para el contorno del botón?
5. ¿De qué tamaño debe ser un botón en un teléfono?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·