Una media query en un ejemplo
Una media query envuelve CSS en una condición. Las reglas dentro de @media (min-width: 600px) solo se aplican mientras el viewport mide al menos 600px de ancho:
La vista previa es un marco, y una media query dentro de él mide el marco, no tu pantalla. En un escritorio mide más o menos lo mismo que esta columna del artículo, normalmente más de 600px, así que la caja es azul. Haz tu ventana del navegador lo bastante angosta como para que el marco baje de 600px y se vuelve ámbar; la línea debajo de la caja informa el ancho del marco en vivo. En un teléfono el marco ya es angosto, así que ves la versión ámbar.
La sintaxis @media
@media (min-width: 600px) { ... } /* one condition */
@media (min-width: 600px) and (max-width: 899px) { ... } /* both must be true */
@media (max-width: 599px), print { ... } /* either one (comma = or) */
@media not print { ... } /* negation */
@media screen and (orientation: landscape) { ... } /* media type plus a feature */
@media (width >= 600px) { ... } /* range syntax */
@media (600px <= width < 900px) { ... } /* a range with both ends */
and necesita un espacio a cada lado: (min-width: 600px)and(max-width: 900px) es inválido y se ignora toda la consulta. La sintaxis de rangos (width >= 600px) es compatible con las versiones actuales de Chrome, Firefox y Safari y se lee con más claridad que min-width, sobre todo para "hasta, sin incluir".
Las características que más vas a usar:
| Característica | Coincide cuando | Ejemplo |
|---|---|---|
min-width / width >= | el viewport mide al menos este ancho | (min-width: 768px) |
max-width / width <= | el viewport mide como máximo este ancho | (max-width: 767px) |
orientation | portrait (más alto que ancho) o landscape | (orientation: landscape) |
hover | el puntero principal puede hacer hover (hover) o no (none, la mayoría de las pantallas táctiles) | (hover: hover) |
pointer | el puntero principal es fine (mouse) o coarse (dedo) | (pointer: coarse) |
prefers-color-scheme | el usuario eligió un tema dark o light | (prefers-color-scheme: dark) |
prefers-reduced-motion | el usuario pidió al sistema menos movimiento | (prefers-reduced-motion: reduce) |
print (un tipo de medio) | la página se imprime o se guarda como PDF | @media print |
Mobile first: breakpoints con min-width
Escribe el layout del teléfono como CSS base y luego agrega consultas min-width que cambian el layout a medida que crece el espacio. Cada consulta solo tiene que describir lo que cambia:
En una ventana de escritorio ancha la vista previa pasa de 720px, así que ves tres columnas; la línea debajo del grid informa el ancho y la cantidad de columnas. Achica la ventana y baja a dos por debajo de 720px y a una por debajo de 480px. El orden de las dos consultas importa: en una pantalla ancha las dos son verdaderas y gana la posterior, así que las consultas min-width van de la más pequeña a la más grande. (Con consultas max-width es al revés: la más grande primero.)
Elegir los breakpoints
No hay una lista oficial. Estos son valores habituales y lo que suelen separar:
| Breakpoint | Normalmente |
|---|---|
min-width: 640px | teléfonos grandes en horizontal, tablets pequeñas |
min-width: 768px | tablets en vertical |
min-width: 1024px | tablets en horizontal, laptops pequeñas |
min-width: 1280px | escritorios |
Un método mejor: empieza con el layout angosto, ensancha la ventana hasta que algo se vea mal (líneas demasiado largas, tarjetas demasiado anchas) y pon un breakpoint ahí. Dos o tres breakpoints bastan para la mayoría de las páginas. A menudo no necesitas ninguno: repeat(auto-fit, minmax(200px, 1fr)) en grid, flex-wrap y clamp() se adaptan de forma continua.
Toda página necesita la etiqueta meta viewport
Sin esta etiqueta en el <head>, los navegadores de los teléfonos acomodan la página como si la pantalla midiera unos 980px de ancho y luego alejan el zoom, así que tu consulta min-width: 480px siempre es verdadera y tu layout de teléfono nunca se ve:
<meta name="viewport" content="width=device-width, initial-scale=1">
La página de meta viewport explica todos sus valores.
Modo oscuro con prefers-color-scheme
prefers-color-scheme sigue la configuración clara u oscura del sistema operativo. La forma más limpia de usarla es guardar los colores en variables CSS y cambiar solo las variables dentro de la consulta:
En esta vista previa, la consulta puede mostrar false aunque tu sistema esté en modo oscuro: Chrome no pasa la preferencia del sistema a un marco con sandbox como este. El botón cambia las mismas variables a mano, que es como funciona el selector de tema de un sitio. :root[data-theme] es más específico que el :root simple de la consulta, así que la elección del lector gana sobre la del sistema. Guarda el ejemplo como archivo y ábrelo para ver cómo la consulta sigue a tu sistema.
El script muestra la segunda mitad de las media queries: matchMedia() ejecuta la misma consulta desde JavaScript, devuelve si coincide en ese momento y dispara un evento change cuando la respuesta cambia. Úsalo cuando el comportamiento (no solo el estilo) depende de la pantalla, como abrir un menú de otra forma en dispositivos táctiles.
El movimiento funciona igual. Envuelve las animaciones grandes o en bucle en una comprobación de prefers-reduced-motion, como se muestra en la página de animaciones.
Container queries: cuando el componente debe decidir
Una media query solo conoce el viewport. Una tarjeta que aparece en una columna principal ancha y en una barra lateral angosta de la misma página necesita conocer su propio espacio. Eso es una container query: marca el padre con container-type: inline-size y usa @container con las mismas condiciones.
Arrastra la esquina de la caja gris para cambiar su tamaño. La tarjeta cambia de layout a los 360px de ancho de la caja, sea cual sea el tamaño de la ventana:
Con sus 480px iniciales, la imagen queda al lado del texto. Arrastra la caja por debajo de 376px (360px de contenido más 8px de padding a cada lado) y la imagen pasa arriba del texto. Las container queries funcionan en todos los navegadores principales actuales.
Estilos para imprimir
@media print se aplica solo cuando la página se imprime o se guarda como PDF. Oculta la navegación y los botones, y usa texto oscuro sobre blanco:
@media print {
nav, .ad, button { display: none; }
body { color: black; background: white; }
a::after { content: " (" attr(href) ")"; } /* print the URL after each link */
}
Errores comunes
- Sin etiqueta meta viewport. Los teléfonos informan entonces un viewport de layout ancho y las consultas de pantalla pequeña nunca coinciden.
- Consultas en el orden equivocado. Dos consultas verdaderas con el mismo selector: gana la posterior. Ordena las consultas
min-widthde menor a mayor. - Límites que se superponen o dejan huecos.
max-width: 768pxymin-width: 768pxcoinciden las dos en exactamente 768px. Usa767pxen una de ellas, o la sintaxis de rangos(width < 768px)y(width >= 768px). - Sin espacio después de
and.and(es inválido y la consulta se descarta en silencio. - Probar solo cambiando el tamaño de una ventana de escritorio. Revisa también un teléfono real o el modo dispositivo del navegador, que aplica el viewport, la densidad de píxeles y el tacto del teléfono.
- Demasiados breakpoints. Si cada componente necesita los suyos, prueba grid con
auto-fit,flex-wrapo container queries.
Preguntas frecuentes
¿Qué es una media query en CSS?
Una regla que aplica el CSS que contiene solo cuando se cumple una condición sobre el dispositivo o la ventana. @media (min-width: 600px) { ... } aplica sus estilos solo cuando el viewport mide al menos 600px de ancho, y así una misma página se adapta de los teléfonos a los escritorios.
¿Uso media queries con min-width o con max-width?
La mayoría de los sitios se escriben mobile first: el CSS base es para pantallas pequeñas y las consultas min-width agregan layout a medida que crece la pantalla. Las consultas max-width funcionan al revés, partiendo del layout de escritorio. Las dos sirven; mezclarlas en el mismo elemento produce superposiciones y huecos en los límites.
¿Cuáles son los breakpoints estándar de las media queries?
No hay un estándar oficial. Los valores habituales rondan los 640px, 768px, 1024px y 1280px, pero la mejor regla es agregar un breakpoint donde tu propio layout empieza a verse mal, y mantener pocos breakpoints.
¿Por qué mi media query no funciona en el celular?
La causa más común es que falta la etiqueta meta viewport. Sin <meta name="viewport" content="width=device-width, initial-scale=1">, los navegadores móviles fingen que la página mide unos 980px de ancho, así que las consultas de ancho pequeño nunca coinciden. Comprueba también que la consulta vaya después de las reglas que sobrescribe.
¿Cómo escribo una media query para el modo oscuro?
Usa @media (prefers-color-scheme: dark) { ... }. Coincide cuando el sistema operativo o el navegador del usuario tiene un tema oscuro, y dentro puedes sobrescribir colores o variables CSS.
¿Qué diferencia hay entre media queries y container queries?
Una media query evalúa el viewport (toda la ventana). Una container query, escrita con @container, evalúa el tamaño de un elemento padre que marcas con container-type: inline-size. Usa container queries para un componente que aparece en lugares anchos y angostos de la misma página.