Saltar al contenido
responsive.css · devschool

Diseño responsive

Lección 14 de 25 · 11 min de lectura · Actualizado el

En esta lección
  1. Paso 1: la etiqueta viewport
  2. Paso 2: nada de anchos fijos
  3. Paso 3: media queries
  4. Otras características que puedes consultar
  5. Mobile first
  6. Puntos de corte
  7. Unidades relativas y tipografía fluida
  8. Imágenes flexibles
  9. Un menú responsive
  10. Media queries frente a container queries
  11. Cómo probarlo
  12. Errores frecuentes
  13. Resumen

Una web responsive se adapta al tamaño de la pantalla: se ve bien en un móvil, en una tablet y en un monitor enorme. Hoy más de la mitad de las visitas llegan desde el móvil, así que no es opcional. En esta lección verás las piezas que lo hacen posible: la etiqueta viewport, el enfoque mobile first, las media queries (también las modernas), las unidades relativas, la tipografía fluida y cómo probarlo todo sin tener diez móviles en la mesa.

Paso 1: la etiqueta viewport

Sin esta línea en el <head>, el móvil muestra la página como si fuera un ordenador, en miniatura:

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

¿Por qué pasa eso? Cuando aparecieron los primeros smartphones, casi ninguna web estaba pensada para ellos. Los navegadores móviles decidieron “fingir” una pantalla de unos 980px de ancho y encoger el resultado. Con width=device-width le dices al navegador: “mi página sí está preparada, usa el ancho real del dispositivo”. Y initial-scale=1 arranca sin zoom.

Cuidado: no añadas user-scalable=no ni maximum-scale=1. Impiden que la gente amplíe la página con los dedos, y eso es un problema grave de accesibilidad para quien ve mal.

Paso 2: nada de anchos fijos

Evita cosas como width: 960px. En un móvil de 390px, se sale de la pantalla y aparece la temida barra de desplazamiento horizontal. Usa en su lugar:

.contenedor {
  max-width: 1100px;  /* como mucho 1100px... */
  margin: 0 auto;     /* ...centrado */
  padding: 0 16px;    /* y con aire en los lados */
}

img {
  max-width: 100%;    /* las imágenes nunca más anchas que su caja */
  height: auto;       /* mantiene la proporción */
}

La idea clave: deja que el contenido fluya y pon solo límites (max-width, min-width). Flexbox y Grid ayudan muchísimo porque reparten el espacio disponible sin que tengas que calcular píxeles. Por ejemplo, repeat(auto-fit, minmax(200px, 1fr)) crea una galería que cambia de columnas sola (lo tienes en Grid).

Paso 3: media queries

Una media query aplica estilos solo si se cumple una condición, normalmente un ancho de pantalla:

/* Estilos para todas las pantallas */
.menu { display: flex; flex-direction: column; }

/* Solo en pantallas de 768px o más */
@media (min-width: 768px) {
  .menu { flex-direction: row; }
}
  • min-width: 768px: pantallas de 768px o más.
  • max-width: 767px: pantallas de 767px o menos.

Las reglas de dentro de la media query no tienen más especificidad que las de fuera. Si ganan es porque están escritas después en el archivo (la cascada, que viste en Cascada, especificidad y herencia). Por eso las media queries van siempre al final de cada bloque o del archivo.

La sintaxis moderna de rangos

Desde 2023 todos los navegadores actuales entienden una forma más legible de escribir los rangos, con operadores de comparación:

/* Igual que (min-width: 600px) */
@media (width >= 600px) { ... }

/* Entre 600px y 899px, de un vistazo */
@media (600px <= width < 900px) {
  .lateral { display: none; }
}

Con min-width y max-width el rango “de 600 a 899” obligaba a escribir (min-width: 600px) and (max-width: 899.98px). La versión nueva se lee como una fórmula de matemáticas y evita el lío de los decimales.

Combinar condiciones

/* Las dos condiciones: and */
@media (width >= 600px) and (orientation: landscape) { ... }

/* Una u otra: coma */
@media (width < 400px), (height < 500px) { ... }

/* Solo al imprimir */
@media print {
  nav, footer { display: none; }
}

Otras características que puedes consultar

Las media queries no solo miran el ancho. Estas son las más útiles:

ConsultaSe cumple cuando…
(orientation: portrait)La ventana es más alta que ancha
(orientation: landscape)La ventana es más ancha que alta
(prefers-color-scheme: dark)El sistema del usuario está en modo oscuro
(prefers-reduced-motion: reduce)El usuario ha pedido menos animaciones
(hover: hover)El puntero principal puede pasar por encima (ratón)
(pointer: coarse)El puntero principal es poco preciso (un dedo)

Modo oscuro automático

Combinado con variables CSS, cambiar de tema es cuestión de pocas líneas:

:root {
  --fondo: #ffffff;
  --texto: #1b1b1b;
}

@media (prefers-color-scheme: dark) {
  :root {
    --fondo: #16181d;
    --texto: #e8e8e8;
  }
}

body {
  background: var(--fondo);
  color: var(--texto);
}

Tienes más sobre esto en Variables CSS.

Menos movimiento

Hay personas a las que las animaciones les marean. Si lo han indicado en su sistema, respétalo:

@media (prefers-reduced-motion: reduce) {
  * { animation: none; transition: none; }
}

Lo ves con más detalle en Transiciones y animaciones.

Ratón o dedo

Un efecto :hover en un móvil no tiene sentido: no hay “pasar por encima”. Y un botón de 24px es fácil de pulsar con ratón, pero no con el dedo:

/* Efecto solo donde hay ratón */
@media (hover: hover) {
  .tarjeta:hover { transform: translateY(-4px); }
}

/* Botones más grandes en pantallas táctiles */
@media (pointer: coarse) {
  .boton { min-height: 44px; padding: 12px 20px; }
}

Esto es mejor que suponer “móvil = táctil”: hay portátiles con pantalla táctil y tablets con ratón.

Mobile first

La forma recomendada es escribir primero el CSS para móvil y añadir cambios para pantallas grandes con min-width. Así:

  • El móvil, que suele tener peor conexión y procesador, aplica el CSS más sencillo.
  • Pensar primero en la pantalla pequeña te obliga a decidir qué es lo importante.
  • El CSS base suele ser una sola columna, que es lo que el HTML hace por defecto. Escribes menos.

Compara los dos enfoques para una rejilla de productos:

/* Mobile first: se añade complejidad al crecer */
.productos { display: grid; gap: 16px; }

@media (width >= 600px) {
  .productos { grid-template-columns: repeat(2, 1fr); }
}
@media (width >= 900px) {
  .productos { grid-template-columns: repeat(4, 1fr); }
}
/* Desktop first: se deshace lo que sobra al encoger */
.productos { display: grid; gap: 16px; grid-template-columns: repeat(4, 1fr); }

@media (width < 900px) {
  .productos { grid-template-columns: repeat(2, 1fr); }
}
@media (width < 600px) {
  .productos { grid-template-columns: 1fr; }
}

Los dos funcionan, pero en el segundo pasas el tiempo anulando lo anterior. Con mobile first casi siempre escribes solo min-width (o width >=).

Puntos de corte

Un punto de corte (breakpoint) es el ancho en el que cambia el diseño. Unos habituales son 600px (tablets pequeñas), 900px (tablets y portátiles pequeños) y 1200px (escritorio). Pero lo mejor es poner el punto de corte donde tu diseño empiece a verse mal, no donde diga una lista.

El método práctico: abre la página, estrecha y ensancha la ventana poco a poco, y en cuanto algo se vea apretado o demasiado estirado, ahí va una media query. No diseñes para “el iPhone 15”: mañana saldrá otro con otro tamaño.

Consejo: con dos o tres puntos de corte basta para casi cualquier web. Si necesitas ocho, probablemente el diseño base no es lo bastante flexible.

Unidades relativas y tipografía fluida

  • %: porcentaje del ancho del elemento padre.
  • rem: relativo al tamaño de letra base (normalmente 16px). Si el usuario aumenta la letra en su navegador, todo crece con él.
  • vw / vh: porcentaje del ancho y el alto de la ventana. 100vh es toda la altura (en móvil, mejor 100dvh).
  • clamp(): un valor que crece con la pantalla pero con mínimo y máximo.

Tienes todas explicadas en Unidades de medida. Aquí nos interesa una aplicación concreta: la tipografía fluida. En lugar de cambiar el tamaño de los títulos a saltos con media queries, dejas que crezca poco a poco:

h1 {
  /* mínimo 1.8rem, ideal 1rem + 4vw, máximo 3.5rem */
  font-size: clamp(1.8rem, 1rem + 4vw, 3.5rem);
}

.seccion {
  /* el espaciado también puede ser fluido */
  padding-block: clamp(2rem, 6vw, 5rem);
}

Sumar rem y vw (en vez de usar solo vw) tiene un porqué: si el valor ideal fuera solo 5vw, al hacer zoom el texto no crecería, porque la ventana no cambia de ancho. Con la parte en rem, el zoom sigue funcionando.

Imágenes flexibles

Con max-width: 100% y height: auto las imágenes ya no rompen el diseño. Pero hay dos mejoras importantes:

.tarjeta img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

Un menú responsive

El caso clásico: en móvil los enlaces van uno debajo de otro; en pantallas anchas, en fila con el logo a la izquierda.

<header class="cabecera">
  <a class="logo" href="/">GymFit</a>
  <nav>
    <ul class="menu">
      <li><a href="/clases">Clases</a></li>
      <li><a href="/horarios">Horarios</a></li>
      <li><a href="/tarifas">Tarifas</a></li>
    </ul>
  </nav>
</header>
.cabecera { padding: 12px 16px; }

.menu {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;   /* móvil: en columna */
  gap: 8px;
}

@media (width >= 700px) {
  .cabecera {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .menu { flex-direction: row; gap: 24px; }
}

Muchas webs esconden el menú del móvil tras un botón “hamburguesa”. Mostrarlo y ocultarlo necesita un poco de JavaScript (o el elemento <details>), pero el CSS de base es exactamente este. Tienes una maqueta completa en el proyecto de maquetación.

Media queries frente a container queries

Las media queries miran la ventana. Pero a veces lo que importa es el hueco donde está un componente: una tarjeta puede estar en una columna estrecha lateral o en el contenido principal ancho, dentro de la misma pantalla. Para eso existen las container queries, que preguntan por el tamaño del contenedor:

.zona-tarjeta { container-type: inline-size; }

@container (width >= 400px) {
  .tarjeta { display: flex; }
}

Úsalas para componentes reutilizables y deja las media queries para el diseño general de la página. Las explicamos a fondo en CSS moderno.

Cómo probarlo

En el navegador, pulsa F12 y activa el modo dispositivo (el icono del móvil y la tablet, o Ctrl + Mayús + M). Podrás:

  • Elegir modelos de móvil o escribir un ancho a mano.
  • Arrastrar el borde para ver en qué ancho se rompe el diseño.
  • Simular una conexión lenta y el modo oscuro (en el panel Rendering, opción prefers-color-scheme).

Aun así, prueba de vez en cuando en un móvil de verdad: el tacto, el tamaño real del texto y la barra del navegador no se simulan del todo.

Errores frecuentes

  • Olvidar la etiqueta viewport. Las media queries parecen no funcionar en el móvil porque este cree que mide 980px.
  • Media queries antes que los estilos base. Como tienen la misma especificidad, la regla que va después las pisa. Ponlas al final.
  • Anchos fijos en píxeles en contenedores, tablas o imágenes, que provocan scroll horizontal. Busca el culpable en DevTools.
  • Mezclar min-width y max-width sin orden, con rangos que se solapan (max-width: 600px y min-width: 600px se cumplen a la vez justo a 600px). La sintaxis width < 600px / width >= 600px lo evita.
  • Ocultar contenido importante en móvil con display: none. Si es importante, reorganízalo; no lo quites.

Resumen

HerramientaPara qué
<meta name="viewport">Que el móvil use su ancho real
max-width, %, Flexbox, GridDiseños fluidos sin anchos fijos
@media (width >= 600px)Cambiar el diseño a partir de un ancho
Mobile firstCSS base para móvil, min-width para crecer
prefers-color-scheme, prefers-reduced-motionRespetar las preferencias del usuario
hover, pointerAdaptar a ratón o dedo
clamp()Tipografía y espacios fluidos
max-width: 100%, srcset, object-fitImágenes flexibles
@containerAdaptar un componente a su hueco
DevTools (modo dispositivo)Probar distintos tamaños

Pruébalo tú

Cambia el código y pulsa Ejecutar (o Ctrl + Enter).

responsive.html
Resultado

Pon a prueba lo que has aprendido

[CSS] ¿Cuándo se aplican estos estilos?
@media (max-width: 600px) {
  .menu { display: none; }
}

[CSS] ¿Qué significa "mobile first"?

[CSS] ¿En qué anchos de ventana se oculta .lateral?
@media (600px <= width < 900px) {
  .lateral { display: none; }
}

[CSS] Quieres que el efecto :hover de las tarjetas solo se aplique en dispositivos con ratón. ¿Qué media query es la adecuada?

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.