Consultas CSS basadas en tamaños: media queries y container queries

Consultas CSS basadas en tamaños: media queries y container queries

Si llevas tiempo trabajando con CSS, seguro que las media queries forman ya parte de tu día a día. Definir un breakpoint, reorganizar columnas, ajustar tamaños, etc. forma parte del flujo normal cuando haces la maquetación de una web responsive.

Durante años, para un diseñador web esa ha sido la forma estándar de adaptar nuestros diseños al tamaño de pantalla. Y sigue funcionando perfectamente. Pero hay un pequeño detalle que muchas veces se nos escapa, porque no siempre queremos que un componente cambie porque la pantalla sea más grande o más pequeña, sino por el espacio real que tiene disponible en ese momento.

Ahí es donde entran en juego las container queries. No sustituyen a las media queries, pero sí introducen una forma distinta de pensar los estilos, más centrada en el componente que en el contexto global.

En este artículo quiero explicarte qué son exactamente las consultas CSS basadas en tamaños, qué diferencias hay entre media queries y container queries, cómo funcionan y en qué casos tiene sentido usar cada una.

Media queries en CSS

Qué es una media query

Una media query es una regla CSS que aplica estilos en función de características del entorno del dispositivo. En la práctica, casi siempre hablamos del tamaño del viewport. Es decir, el ancho de la ventana del navegador o de la pantalla del dispositivo.

Su sintaxis se basa en usar @media seguido de paréntesis, que es donde especificaremos la condición que queremos que se cumpla para que se apliquen los estilos.

Un ejemplo muy típico sería este:

@media (min-width: 768px) {
  .contenedor {
    display: flex;
  }
}

Aquí la lógica es sencilla y cuando la ventana tenga al menos 768 píxeles de ancho, el .contenedor pasa a mostrarse en flex.

Lo importante no es tanto la sintaxis como entender qué está evaluando realmente la media query: el tamaño del viewport. Es decir, el contexto global de la página.

Además del ancho, también podemos evaluar otras características, como la orientación del dispositivo o las preferencias del usuario (por ejemplo, si tiene activado el modo oscuro). Pero en el día a día, cuando hablamos de media queries, casi siempre estamos hablando de breakpoints basados en anchura.

Y eso ha sido, durante bastante tiempo, más que suficiente para construir diseños web responsive sólidos.

Cómo funcionan realmente

Cuando el navegador encuentra una media query, no la ejecuta como si fuera un bloque aislado. Lo que hace es evaluar constantemente si la condición se cumple o no.

Si se cumple, aplica las reglas que hay dentro. Si deja de cumplirse (por ejemplo, al redimensionar la ventana), esas reglas dejan de aplicarse.

Esto es importante porque las media queries no sustituyen el CSS anterior, sino que lo complementan o lo sobrescriben según la cascada.

Por ejemplo:

.contenedor {
  display: block;
}
@media (min-width: 768px) {
  .contenedor {
    display: flex;
  }
}

Aquí el comportamiento es progresivo. Por defecto, el contenedor es block, pero cuando el viewport alcanza los 768px, pasa a flex. No estamos cambiando el estilo en sentido literal, estamos añadiendo una regla que solo existe cuando se cumple la condición.

Esto explica por qué el enfoque mobile-first sigue siendo tan recomendable. Definimos primero el estilo base (para pantallas pequeñas) y después vamos ampliando con min-width.

También conviene entender que varias media queries pueden convivir (y de hecho conviven habitualmente), así como encadenarse:

@media (min-width: 768px) { ... }
@media (min-width: 1024px) { ... }
@media (min-width: 1280px) { ... }

Cada una se activará cuando corresponda y el resultado final dependerá de la cascada, la especificidad y el orden en el que estén declaradas.

En proyectos pequeños esto suele ser sencillo de mantener. En proyectos grandes, con muchos breakpoints y componentes distintos, la cosa se puede volver bastante más compleja. Y ahí empieza a notarse que todo depende del tamaño global de la pantalla, no del contexto real del componente.

Limitaciones de las media queries

Las media queries funcionan muy bien cuando el diseño depende claramente del tamaño de la pantalla. Ahí no hay discusión. Para definir la estructura general de una página siguen siendo la herramienta adecuada.

El problema aparece cuando empezamos a trabajar con componentes reutilizables.

Imagina una tarjeta de producto que puede aparecer en distintos contextos:

  • En un grid principal a tres o cuatro columnas.
  • En una columna lateral más estrecha.
  • Dentro de un bloque promocional embebido en otra página.

Con media queries solo podemos condicionar estilos en función del viewport. Es decir, el comportamiento del componente siempre dependerá del tamaño de la pantalla, no del espacio real que tenga disponible. Esto genera situaciones que pueden generar un comportamiento no deseado.

Puede ocurrir que la pantalla sea grande, pero el componente esté dentro de una columna estrecha. Según la media query, debería mostrarse en modo amplio, pero en la práctica no tiene espacio suficiente. El resultado suele ser un diseño forzado o ajustes adicionales para corregirlo.

Al final, empiezan a aparecer soluciones como breakpoints adicionales, clases específicas para ciertos contextos o reglas más complejas de lo necesario. Vamos, que empiezas a hacer apaños aquí y allá para corregir cosas.

En proyectos pequeños esto se puede gestionar sin demasiado problema. Pero en desarrollos más grandes, especialmente cuando trabajas con sistemas de diseño o componentes modulares, esa dependencia del viewport empieza a ser una limitación clara. Y ahí es donde las container queries aportan algo diferente.

Container queries en CSS

Qué son las container queries

Las container queries nacen precisamente para resolver esa limitación que acabamos de ver. En lugar de preguntar «¿cuánto mide la pantalla?», lo que preguntan es «¿cuánto mide este contenedor concreto?». Ese matiz cambia bastante las cosas a la hora de maquetar.

Con una container query, un componente puede adaptar su diseño en función del espacio real que tiene disponible en ese momento, sin depender del tamaño global de la página.

Eso permite que un mismo componente se comporte de forma distinta según el contexto en el que se inserte, sin necesidad de añadir clases adicionales ni depender de breakpoints globales.

Pero hay un detalle importante. A diferencia de las media queries, aquí no basta con escribir la condición. Antes tenemos que indicar explícitamente qué elemento puede actuar como contenedor de referencia. Si no lo hacemos, la consulta no tiene sobre qué evaluarse.

Es un pequeño requisito extra, pero tiene sentido. Estamos cambiando el punto de referencia del entorno global al contexto local, y eso exige que lo definamos.

Cómo declarar un contenedor

A diferencia de las media queries, las container queries no funcionan porque sí. Antes de poder evaluar el tamaño de un elemento, tenemos que decirle al navegador que ese elemento puede actuar como contenedor de referencia. Y eso se hace con la propiedad container-type:

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

Con esta línea estamos indicando que .tarjeta-producto puede ser evaluado en función de su tamaño horizontal.

Aquí hay dos valores principales que conviene entender bien:

  • inline-size permite evaluar el ancho del contenedor.
  • size permite evaluar tanto ancho como alto.

En la mayoría de los casos prácticos, inline-size es suficiente. El ancho suele ser el factor determinante cuando hablamos de adaptar layouts.

Es importante no pasar por alto este paso. Si escribes una @container sin haber declarado antes un container-type, simplemente no funcionará. No dará error visible, pero la condición nunca se activará.

También conviene tener claro que la container query se evaluará respecto al contenedor más cercano que tenga definido container-type. Es decir, el contexto importa, y bastante.

Cómo escribir una container query

Una vez que hemos declarado el contenedor con container-type, ya podemos utilizar la regla @container. La sintaxis recuerda bastante a una media query, pero en lugar de @media usamos @container:

@container (min-width: 300px) {
.tarjeta-producto {
display: flex;
}
}

A simple vista puede parecer lo mismo que una media query. Pero aquí la condición no se está evaluando sobre el viewport, sino sobre el contenedor más cercano que tenga definido container-type.

Si .tarjeta-producto tiene container-type: inline-size, entonces la condición (min-width: 300px) se evaluará en función del ancho real de esa tarjeta. No importa si la pantalla mide 1920px o 480px. Lo único que importa es el espacio que tenga ese bloque en ese momento.

Y como ocurre con las media queries, también podemos usar condiciones como max-width o combinar varias reglas si el diseño lo necesita. La diferencia no está en la sintaxis, sino en el punto de referencia.

En el siguiente apartado lo vemos con un ejemplo completo para que se entienda mejor en un caso real.

Ejemplo práctico completo

Para entender bien la diferencia, vamos a bajar esto a algo muy concreto. Imagina una tarjeta de producto sencilla. Algo que podría aparecer en un listado, en una sidebar o dentro de un bloque destacado.

El HTML podría ser así:

<div class="tarjeta-producto">
<img src="producto.jpg" alt="Producto">
<div class="contenido">
<h2>Nombre del producto</h2>
<p>Descripción breve del producto.</p>
</div>
</div>

Partimos de un estilo base pensado para espacios pequeños:

.tarjeta-producto {
container-type: inline-size;
border: 1px solid #ccc;
padding: 16px;
}.tarjeta-producto img {
width: 100%;
height: auto;
}

En este estado, la tarjeta se muestra en vertical (magen arriba, contenido debajo). Es un patrón que funciona bien cuando el ancho es limitado.

Ahora añadimos una condición para cuando el componente tenga más espacio disponible:

@container (min-width: 400px) {
.tarjeta-producto {
display: flex;
gap: 16px;
} .tarjeta-producto img {
width: 150px;
}
}

Si la tarjeta supera los 400 píxeles de ancho, reorganizamos el contenido en horizontal. La imagen pasa a ocupar un ancho fijo y el texto se coloca al lado.

Lo interesante aquí no es el cambio de layout en sí, que es bastante habitual. Lo importante es que este comportamiento depende exclusivamente del espacio que tenga la tarjeta, no del tamaño de la pantalla.

Eso permite colocar este mismo componente en distintos contextos y que se adapte sin necesidad de ajustes adicionales. Deja de depender del viewport y se vuelve más fácil de reutilizar y mantener cuando el proyecto crece.

Buenas prácticas al trabajar con consultas basadas en tamaño

Conviene seguir pensando en mobile-first como base si el proyecto es complejo. Definir primero el comportamiento mínimo del componente y, a partir de ahí, ampliarlo cuando haya más espacio disponible. Eso sigue siendo una buena estrategia, tanto con media queries como con container queries.

También ayuda mantener coherencia en los umbrales. No tiene mucho sentido declarar condiciones arbitrarias sin un criterio claro. Si decides que ciertos componentes cambian a partir de 400 píxeles, intenta que esa lógica tenga continuidad en el resto del sistema.

Evita, sobre todo, usar container queries como si fueran una solución mágica. No todos los bloques necesitan comportamientos complejos. A veces una estructura simple basada en media queries es más que suficiente.

Con el tiempo, lo que realmente marca la diferencia no es la técnica concreta, sino cómo planteas la arquitectura del CSS. Tener una base coherente y predecible facilita mucho el mantenimiento cuando el proyecto crece.

En mi caso, como diseñador maquetador web freelance, he comprobado que este cambio de mentalidad resulta especialmente útil cuando trabajas con sistemas modulares o desarrollos a medida donde los bloques se reutilizan constantemente. Cuanto más clara es la estructura, menos dependes de correcciones posteriores.


Referencias:

Jesús Tovar - Desarrollador web freelance Sevilla

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *