La mayoría de los sitios web modernos utilizan técnicas de diseño web responsivo para garantizar que se vean bien, sean legibles y sigan siendo utilizables en dispositivos con cualquier tamaño de pantalla, es decir, teléfonos móviles, tabletas, ordenadores portátiles, monitores de PC de escritorio, televisores, proyectores, etc.

Los sitios que utilizan estas técnicas tienen una única plantilla, que modifica el diseño en función de las dimensiones de la pantalla:

  • Las pantallas más pequeñas suelen mostrar una vista lineal de una sola columna en la que los controles de la interfaz de usuario, como los menús, se activan haciendo clic en los iconos (hamburguesa).
  • Las pantallas más grandes muestran más información, quizás con barras laterales alineadas horizontalmente. Los controles de la interfaz de usuario, como los elementos del menú, pueden estar siempre visibles para facilitar el acceso.

Una gran parte del diseño web responsivo es la implementación de una Media Queries (consulta de medios) CSS o JavaScript para detectar el tamaño del dispositivo y servir automáticamente el diseño apropiado para ese tamaño. Vamos a discutir por qué estas consultas son importantes y cómo trabajar con ellas, pero primero, vamos a discutir el diseño responsivo en general.

¿Por qué es importante el diseño web responsivo?

Es imposible ofrecer un único diseño de página y esperar que funcione en todas partes.

Cuando los teléfonos móviles obtuvieron por primera vez un acceso rudimentario a la web, a principios de la década de 2000, los propietarios de sitios web solían crear dos o tres plantillas de página separadas, basadas vagamente en las vistas de móvil y de escritorio. Esto se volvió cada vez más impracticable a medida que la variedad de dispositivos crecía exponencialmente.

Hoy en día, existen numerosos tamaños de pantalla que van desde las diminutas pantallas de los relojes de pulsera hasta los enormes monitores de 8 K y muchos más. Incluso si solo se tienen en cuenta los teléfonos móviles, los dispositivos más recientes pueden tener una resolución superior a la de muchos ordenadores portátiles de gama baja.

El uso de los móviles también ha crecido más que el de los ordenadores de sobremesa. A menos que tu sitio tenga un conjunto específico de usuarios, puede esperar que la mayoría de la gente acceda a él desde un smartphone. Los dispositivos de pantalla pequeña han dejado de ser una ocurrencia tardía y deben tenerse en cuenta desde el principio, a pesar de que la mayoría de los diseñadores web, desarrolladores y clientes siguen utilizando un PC estándar.

Google ha reconocido la importancia de los dispositivos móviles. Los sitios se clasifican mejor en la búsqueda de Google cuando son utilizables y tienen un buen rendimiento en un smartphone. Un buen contenido sigue siendo vital, pero un sitio que cargue con lentitud y no se adapte a las dimensiones de la pantalla de sus usuarios podría perjudicar a tu negocio.

Por último, ten en cuenta la accesibilidad. Un sitio que funciona para todos, independientemente del dispositivo que utilicen, llegará a un público más amplio. La accesibilidad es un requisito legal en muchos países, pero incluso si no lo es donde te encuentras, ten en cuenta que un mayor número de espectadores se traducirá en más conversiones y mayor rentabilidad.

¿Cómo funciona el diseño responsivo?

La base del diseño responsivo son las consultas de medios: una tecnología CSS que puede aplicar estilos en función de métricas como el tipo de salida (pantalla, impresora o incluso voz), las dimensiones de la pantalla, la relación de aspecto de la pantalla, la orientación del dispositivo, la profundidad del color y la precisión del puntero. Las consultas de medios también pueden tener en cuenta las preferencias del usuario, como la reducción de animaciones, el modo claro/oscuro y un mayor contraste.

Los ejemplos que hemos mostrado demuestran las consultas de medios utilizando solo el ancho de la pantalla, pero los sitios pueden ser considerablemente más flexibles. Consulta el conjunto completo de opciones en MDN para más detalles.

El soporte de Media query es excelente y ha estado en los navegadores por más de una década. Solo IE8 y los inferiores no tienen soporte. Ignoran los estilos aplicados por las consultas de medios, pero esto a veces puede ser una ventaja (lee más en la sección de Mejores Prácticas más adelante).

Existen tres formas estándar de aplicar estilos mediante consultas de medios. La primera carga hojas de estilo específicas en el código HTML. Por ejemplo, la siguiente etiqueta carga la hoja de estilo wide.css cuando un dispositivo tiene una pantalla de al menos 800 píxeles de ancho:

<link rel="stylesheet" media="screen and (min-width: 800px)" href="wide.css" />

En segundo lugar, las hojas de estilo pueden cargarse condicionalmente en los archivos CSS utilizando una regla @import:

/* main.css */
@import url('wide.css') screen and (min-width: 800px);

Lo más habitual es aplicar las consultas de medios en las hojas de estilo mediante un bloque de reglas @media CSS que modifica estilos específicos. Por ejemplo:

/* default styles */
main {
  width: 400px;
}

/* styles applied when screen has a width of at least 800px */
@media screen and (min-width: 800px) {
  main {
    width: 760px;
  }
}

Los desarrolladores pueden aplicar las reglas de media query que sean necesarias para adaptar el diseño de un sitio.

Las mejores prácticas de Media Query

Cuando se idearon las consultas de medios, muchos sitios optaron por un conjunto de diseños rígidos y fijos. Esto es conceptualmente más fácil de diseñar y codificar porque replica efectivamente un conjunto limitado de plantillas de página. Por ejemplo:

  1. Los anchos de pantalla inferiores a 600px utilizan un diseño similar al de los móviles de 400px.
  2. Los anchos de pantalla entre 600px y 999px utilizan un diseño tipo tableta de 600px de ancho.
  3. Los anchos de pantalla superiores a 1.000px utilizan un diseño similar al de un ordenador de sobremesa de 1.000px.

La técnica es defectuosa. Los resultados en pantallas muy pequeñas y muy grandes pueden parecer pobres, y el mantenimiento de CSS puede ser necesario a medida que los dispositivos y los tamaños de pantalla cambian con el tiempo.

Una mejor opción es utilizar un diseño fluido «mobile-first» con puntos de ruptura que adapten el diseño a determinados tamaños. En esencia, el diseño por defecto utiliza los estilos más sencillos para pantallas pequeñas que posicionan los elementos en bloques verticales lineales.

Por ejemplo, <article> y <aside> dentro de un contenedor <main>:

/* default small-screen device */
main {
  width: 100%;
}

article, aside {
  width: 100%;
  padding: 2em;
}

Este es el resultado en todos los navegadores, incluso en los más antiguos que no soportan media queries:

Ejemplo de captura de pantalla sin soporte de media query
Ejemplo de captura de pantalla sin soporte de media query

Cuando se admiten las consultas de medios y la pantalla supera una anchura específica, digamos 500px, los elementos <article> y <aside> pueden posicionarse horizontalmente. Este ejemplo utiliza una cuadrícula CSS, en la que el contenido principal utiliza aproximadamente dos tercios de la anchura, y el contenido secundario utiliza el tercio restante:

/* larger device */
@media (min-width: 500px) {
  main {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 2em;
  }

  article, aside {
    width: auto;
    padding: 0;
  }
}

Aquí está el resultado en pantallas más grandes:

Captura de pantalla de ejemplo con soporte de media query.
Captura de pantalla de ejemplo con soporte de media query.

Alternativas a Media Queries

Los diseños con capacidad de respuesta también pueden implementarse en el CSS moderno utilizando nuevas propiedades que adaptan intrínsecamente el diseño sin examinar las dimensiones de la ventana gráfica. Las opciones incluyen:

  • calc, min-width, max-width, min-height, max-height, y la más reciente propiedad clamp pueden definir dimensiones que dimensionan elementos de acuerdo a límites conocidos y al espacio disponible.
  • Las unidades de la ventana gráfica vw, vh, vmin y vmax pueden dimensionar los elementos según las fracciones de las dimensiones de la pantalla.
  • El texto puede mostrarse en columnas CSS que aparecen o desaparecen según lo permita el espacio.
  • Los elementos pueden ser dimensionados de acuerdo a los tamaños de sus elementos hijos utilizando las dimensiones min-content, fit-content y max-content.
  • CSS flexbox puede envolver – o no envolver – elementos cuando empiezan a exceder el espacio disponible.
  • Los elementos de la cuadrícula CSS pueden tener un tamaño proporcional a las unidades de fracción fr. La función CSS de repetición puede utilizarse junto con minmax, auto-fit y auto-fill para asignar el espacio disponible.
  • Las nuevas y (actualmente) experimentales consultas de contenedores CSS pueden reaccionar al espacio parcial disponible para un componente dentro de un diseño.

Estas opciones están más allá del alcance de este artículo, pero a menudo son más prácticas que las consultas de medios más crudas, que solo pueden responder a las dimensiones de la pantalla. Si puedes lograr un diseño sin consultas de medios, probablemente utilizará menos código, será más eficiente y requerirá menos mantenimiento con el tiempo.

Dicho esto, hay situaciones en las que las consultas de medios siguen siendo la única opción de diseño viable. Siguen siendo esenciales cuando hay que tener en cuenta otros factores de la pantalla, como las relaciones de aspecto, la orientación del dispositivo, la profundidad del color, la precisión del puntero o las preferencias del usuario, como la reducción de las animaciones y el modo luz/oscuridad.

¿Necesitas Media Queries en JavaScript?

Hasta ahora hemos hablado principalmente de CSS. Esto se debe a que la mayoría de los problemas de diseño pueden -y deben– resolverse únicamente con CSS.

Sin embargo, hay situaciones en las que es práctico utilizar una consulta de medios de JavaScript en lugar de CSS, como cuando:

  • Un componente, como un menú, tiene una funcionalidad diferente en pantallas pequeñas y grandes.
  • El cambio a y desde retrato/paisaje afecta a la funcionalidad de una aplicación web.
  • Un juego táctil tiene que cambiar la disposición del <canvas> o adaptar los botones de control.
  • Una aplicación web se adhiere a las preferencias del usuario, como el modo oscuro/luz, la reducción de la animación, la tosquedad del tacto, etc.

Las siguientes secciones demuestran tres métodos que utilizan consultas de medios – u opciones similares a las consultas de medios – en JavaScript. Todos los ejemplos devuelven una cadena de estado donde:

  • vista pequeña = una pantalla con una anchura inferior a 400 píxeles;
  • vista media = una pantalla con una anchura entre 400 y 799 píxeles; y
  • vista grande = una pantalla con una anchura de 800 píxeles o más.

Opción 1: Controlar las dimensiones de la ventana gráfica

Esta es la única opción en los días oscuros antes de que se implementaran las consultas de medios. JavaScript escuchaba los eventos de «redimensionamiento» del navegador, analizaba las dimensiones de la ventana gráfica utilizando window.innerWidth y window.innerHeight (o document.body.clientWidth y document.body.clientHeight en los antiguos IE), y reaccionaba en consecuencia.

Este código muestra en la consola la cadena pequeña, mediana o grande calculada:

const
  screen = {
    small: 0,
    medium: 400,
    large: 800
  };

// observe window resize
window.addEventListener('resize', resizeHandler);

// initial call
resizeHandler();

// calculate size
function resizeHandler() {

  // get window width
  const iw = window.innerWidth;
 
  // determine named size
  let size = null;
  for (let s in screen) {
    if (iw >= screen[s]) size = s;
  }

  console.log(size);
}

Puede ver una demostración en funcionamiento aquí. (Si utilizas un navegador de escritorio, abre este enlace en una nueva ventana para facilitar el cambio de tamaño. Los usuarios de móviles pueden girar el dispositivo).

El ejemplo anterior examina el tamaño de la ventana gráfica a medida que se redimensiona el navegador; determina si es pequeño, mediano o grande; y lo establece como una clase en el elemento body, que cambia el color de fondo.

Las ventajas de este método son:

  • Funciona en todos los navegadores que pueden ejecutar JavaScript, incluso en aplicaciones antiguas.
  • Captas las dimensiones exactas y puedes reaccionar en consecuencia.

Las desventajas:

  • Es una técnica antigua que requiere un código considerable.
  • ¿Es demasiado exacto? ¿Realmente necesita saber cuándo el ancho es de 966px frente a 967px?
  • Es posible que tenga que hacer coincidir manualmente las dimensiones con la correspondiente consulta de medios CSS.
  • Los usuarios pueden cambiar el tamaño del navegador rápidamente, haciendo que la función del manejador se ejecute de nuevo cada vez. Esto puede sobrecargar a los navegadores más antiguos y lentos al estrangular el evento. Solo puede activarse una vez cada 500 milisegundos.

En resumen, no controle las dimensiones de la ventana gráfica a menos que tenga requisitos de tamaño muy específicos y complejos.

Opción 2: Define y supervisa una propiedad personalizada CSS (variable)

Esta es una técnica ligeramente inusual que cambia el valor de una cadena de propiedades personalizadas en CSS cuando se activa una consulta de medios. Las propiedades personalizadas son compatibles con todos los navegadores modernos (pero no con IE).

En el siguiente ejemplo,  --screen custom property se establece como «pequeña», «mediana» o «grande» dentro de un bloque de código @media:

body {
  --screen: "small";
  background-color: #cff;
  text-align: center;
}

@media (min-width: 400px) {
 
  body {
    --screen: "medium";
    background-color: #fcf;
  }
 
}

@media (min-width: 800px) {
 
  body {
    --screen: "large";
    background-color: #ffc;
  }
 
}

El valor puede ser emitido en CSS solo utilizando un pseudo-elemento (pero ten en cuenta que debe estar contenido entre comillas simples o dobles):

p::before {
  content: var(--screen);
}

Puedes obtener el valor de la propiedad personalizada utilizando JavaScript:

const screen = getComputedStyle(window.body)
                 .getPropertyValue('--screen');

Sin embargo, esto no es todo, porque el valor devuelto contiene todos los espacios en blanco y las comillas definidas después de los dos puntos en el CSS. La cadena será ‘ «grande»‘, por lo que es necesario un poco de orden:

// returns small, medium, or large in a string
const screen = getComputedStyle(window.body)
                 .getPropertyValue('--screen')
                 .replace(/\W/g, '');

Puedes ver una demostración en funcionamiento aquí. (Si utiliza un navegador de escritorio, abra este enlace en una nueva ventana para facilitar el cambio de tamaño. Los usuarios de móviles pueden girar el dispositivo).

El ejemplo examina el valor CSS cada dos segundos. Requiere un poco de código JavaScript, pero es necesario para sondear los cambios – no se puede detectar automáticamente que el valor de la propiedad personalizada ha cambiado usando CSS.

Tampoco es posible escribir el valor en un pseudo-elemento y detectar el cambio utilizando un Observador de Mutación DOM. Los pseudoelementos no son una parte «real» del DOM.

Las ventajas:

  • Es una técnica sencilla que utiliza principalmente CSS y coincide con las consultas de medios reales.
  • Cualquier otra propiedad CSS puede ser modificada al mismo tiempo.
  • No es necesario duplicar o analizar las cadenas de consulta de medios de JavaScript.

La principal desventaja es que no se puede reaccionar automáticamente a un cambio en la dimensión de la ventana gráfica del navegador. Si el usuario gira su teléfono de orientación vertical a horizontal, el JavaScript nunca lo sabrá. Se puede sondear con frecuencia los cambios, pero eso es ineficiente y da lugar al retraso que se ve en nuestra demostración.

La supervisión de las propiedades personalizadas de CSS es una técnica novedosa, pero solo es práctica cuando:

  1. El diseño puede fijarse en el punto en el que la página se renderiza inicialmente. Un quiosco o un terminal de punto de venta es una posibilidad, pero es probable que tengan resoluciones fijas y un único diseño, por lo que las consultas de medios de JavaScript son irrelevantes.
  2. El sitio o la aplicación ya ejecuta funciones frecuentes basadas en el tiempo, como la animación de un juego. La propiedad personalizada podría comprobarse al mismo tiempo para determinar si se requieren cambios de diseño.

Opción 3: Utiliza la API matchMedia

La API matchMedia es un poco inusual, pero permite implementar una consulta de medios en JavaScript. Es compatible con la mayoría de los navegadores a partir de IE10. El constructor devuelve un objeto MediaQueryList que tiene una propiedad matches que evalúa a true o false para tu consulta de medios específica.

El siguiente código da como resultado verdadero cuando el ancho de la ventana gráfica del navegador es de 800px o mayor:

const mqLarge  = window.matchMedia( '(min-width: 800px)' );
console.log( mqLarge.matches );

Se puede aplicar un evento «change» al objeto MediaQueryList. Esto se dispara cada vez que el estado de la propiedad matches cambia: Se convierte en verdadero (más de 800px) después de ser previamente falso (menos de 800px) o viceversa.

A la función de recepción se le pasa el objeto MediaQueryList como primer parámetro:

const mqLarge  = window.matchMedia( '(min-width: 800px)' );
mqLarge.addEventListener('change', mqHandler);

// media query handler function
function mqHandler(e) {
 
  console.log(
    e.matches ? 'large' : 'not large'
  );
 
}

El manejador solo se ejecuta cuando la propiedad matches cambia. No se ejecutará cuando la página se cargue inicialmente, así que puedes llamar a la función directamente para determinar el estado inicial:

// initial state
mqHandler(mqLarge);

La API funciona bien cuando se mueve entre dos estados distintos. Para analizar tres o más estados, como el pequeño, el mediano y el grande, requerirá más código.

Comience por definir un objeto de estado de pantalla con objetos matchMedia asociados:

const
  screen = {
    small : null,
    medium: window.matchMedia( '(min-width: 400px)' ),
    large : window.matchMedia( '(min-width: 800px)' )
  };

No es necesario definir un objeto matchMedia en el estado pequeño porque el manejador de eventos del medio se activará al pasar de pequeño a medio.

A continuación, se pueden establecer escuchas de eventos para los eventos medianos y grandes. Estos llaman a la misma función manejadora mqHandler():

// media query change events
for (let [scr, mq] of Object.entries(screen)) {
  if (mq) mq.addEventListener('change', mqHandler);
}

La función manejadora debe comprobar todos los objetos MediaQueryList para determinar si el tamaño pequeño, mediano o grande está actualmente activo. Las coincidencias deben ser ejecutadas en orden de tamaño, ya que un ancho de 999px coincidiría tanto con el mediano como con el grande – solo el más grande debe «ganar»:

// media query handler function
function mqHandler() {
 
  let size = null;
  for (let [scr, mq] of Object.entries(screen)) {
    if (!mq || mq.matches) size = scr;
  }
 
  console.log(size);
 
}

Puede ver una demostración en funcionamiento aquí. (Si utilizas un navegador de escritorio, abra este enlace en una nueva ventana para facilitar el cambio de tamaño. Los usuarios de móviles pueden girar el dispositivo).

Los ejemplos de uso son:

  1. Consultas de medios en CSS para establecer y mostrar una propiedad personalizada (como se muestra en la opción 2 anterior).
  2. Consultas de medios idénticas en objetos matchMedia para controlar los cambios de dimensión en JavaScript. La salida de JavaScript cambiará exactamente al mismo tiempo.

Las principales ventajas de utilizar la API matchMedia son

  • Se basa en los eventos y es eficiente en el procesamiento de los cambios de consulta de medios.
  • Utiliza cadenas de consulta de medios idénticas a las de CSS.

Las desventajas:

  • Manejar dos o más consultas de medios requiere más reflexión y lógica de código.
  • Probablemente necesites duplicar las cadenas de consulta de medios en el código CSS y JavaScript. Esto podría dar lugar a errores si no los mantienes sincronizados.

Para evitar desajustes en las consultas de medios, puedes considerar el uso de tokens de diseño en tu sistema de construcción. Las cadenas de consulta de medios se definen en un archivo JSON (o similar) y los valores se introducen en el código CSS y JavaScript en el momento de la compilación.

En resumen, la API matchMedia es probablemente la forma más eficiente y práctica de implementar una consulta de medios en JavaScript. Tiene algunas peculiaridades, pero es la mejor opción en la mayoría de las situaciones.

Resumen

Las opciones intrínsecas de tamaño de CSS son cada vez más viables, pero las consultas de medios siguen siendo la base del diseño web responsivo para la mayoría de los sitios. Siempre serán necesarias para gestionar diseños más complejos y preferencias de los usuarios, como el modo claro/oscuro.

Intenta mantener las consultas de medios solo en CSS siempre que sea posible. Cuando no tengas más remedio que aventurarte en el terreno de JavaScript, la API matchMedia proporciona un control adicional para los componentes de consulta de medios de JavaScript, que requieren una funcionalidad adicional basada en las dimensiones.

¿Tienes algún otro consejo para implementar una consulta de medios de JavaScript? Compártelos en la sección de comentarios.

Craig Buckler

Freelance UK web developer, writer, and speaker. Has been around a long time and rants about standards and performance.