Frontend 7 jul 2026 · 7 min de lectura

Container queries: el día que dejé de mentirle a mis componentes

Yohangel Ramos

Yohangel Ramos

Tech Lead · Senior Fullstack Developer

Un componente reutilizable promete algo simple: lo pones donde sea y funciona. Durante años esa promesa era mentira, y la mentira tenía nombre: media query. Una tarjeta que se adapta con @media (min-width: 768px) no reacciona a su propio espacio, reacciona al ancho de la ventana. Ponla en un sidebar estrecho con la pantalla en 1400px y la tarjeta cree que tiene sitio de sobra, porque está mirando la variable equivocada. Las container queries arreglan esto de raíz: el componente responde al tamaño de su contenedor, no del viewport. Adopté esto en el design system de un producto real y me ahorró la clase de bug más frustrante de mantener, el de "se ve bien en la demo y mal en su sitio real". Este artículo es por qué el cambio importa más de lo que parece.

La mentira del media query

El problema es de nivel de abstracción. Un componente bien hecho no sabe nada del mundo exterior: recibe props, se pinta, y se supone que se adapta a donde lo coloques. Pero un media query rompe ese encapsulamiento por completo, porque consulta un estado global —el ancho del viewport— que el componente no controla ni debería conocer.

El resultado es que el mismo componente necesita saber en qué layout va a vivir. La tarjeta que se ve perfecta en la grid principal de tres columnas se rompe en el sidebar, no porque esté mal hecha, sino porque su breakpoint asume un ancho de ventana que no se corresponde con el ancho que realmente tiene. Acabas metiendo props como variant="compact" para decirle a mano lo que debería poder deducir solo. Cada uno de esos props es una fuga de la abstracción.

Cómo funciona: el contenedor declara, el hijo consulta

El mecanismo tiene dos partes. Primero declaras que un elemento es un contenedor de consulta. Después, dentro, consultas su tamaño en vez del de la pantalla.

.card-wrapper {
  container-type: inline-size;
  container-name: card;
}

.card {
  display: grid;
  grid-template-columns: 1fr;
}

@container card (min-width: 400px) {
  .card {
    grid-template-columns: 120px 1fr;
  }
}

Lo que cambia respecto al media query es sutil y enorme a la vez: @container card (min-width: 400px) no pregunta "¿es la pantalla ancha?" sino "¿tiene mi contenedor al menos 400px?". La misma tarjeta, sin una sola prop extra, se pinta en una columna dentro de un sidebar de 300px y en dos columnas dentro de una grid amplia. El componente por fin se adapta a su realidad, no a una suposición sobre la ventana.

El caso que me convenció: una card en tres sitios distintos

En el design system tenía una ProductCard que aparecía en tres contextos: la grid de catálogo (ancha), un carrusel de recomendados (medio) y un sidebar de "vistos recientemente" (estrecho). Con media queries tenía tres variantes y un if en el consumidor decidiendo cuál usar según dónde iba. Tres caminos de código para una sola tarjeta.

Con container queries borré las variantes. La ProductCard pasó a ser una sola implementación que consulta su contenedor y se reorganiza sola. El componente dejó de necesitar contexto sobre su ubicación, que es exactamente lo que un componente debería no necesitar. Menos props, menos ramas, menos superficie donde algo se puede desincronizar.

💡 Si tu componente necesita una prop variant solo para caber en distintos anchos, no tienes un problema de diseño de API: tienes un media query donde debería haber una container query. Arréglalo abajo y la prop desaparece sola.

Trade-offs que acepté

No todo es gratis. Declarar container-type: inline-size crea un contexto de contención de tamaño, y eso tiene una consecuencia que hay que entender: el contenedor deja de dimensionarse según su contenido en el eje consultado. En la práctica esto rara vez muerde si envuelves con un wrapper dedicado en lugar de convertir en contenedor un elemento que ya hacía otras cosas, pero es un pie de página real, no un detalle que puedas ignorar.

También acepté un poco más de anidamiento en el marcado. El patrón limpio es un wrapper que es el contenedor y un hijo que es lo que se estiliza; consultar el tamaño de un elemento y a la vez estilizar ese mismo elemento con la consulta no funciona bien. Un div más por componente es un precio que pago con gusto a cambio de borrar toda una categoría de props de contexto.

Por qué esto es más que una comodidad

Lo que de verdad cambió no fue el CSS, fue dónde vive la responsabilidad. Con media queries, la responsabilidad de que un componente se vea bien está repartida entre el componente y quien lo coloca: el consumidor tiene que elegir la variante correcta para el hueco. Con container queries, la responsabilidad vuelve entera al componente, que es donde pertenece.

Esa es la diferencia entre un design system que escala y uno que se convierte en un catálogo de casos especiales. Cada vez que un componente necesita saber dónde va para verse bien, has creado un acoplamiento entre la pieza y su contexto. Las container queries cortan ese acoplamiento, y con él, la clase de deuda que no aparece en ningún ticket pero te ralentiza en cada pantalla nueva.

Yohangel Ramos

Escrito por Yohangel Ramos

Senior Fullstack Developer y Tech Lead. Construyo con React, Next.js, Nest.js y AWS — y escribo sobre lo que aprendo en el camino.

Hablemos →

Sigue leyendo

IA

Embeddings más baratos: recortar dimensiones y cuantizar sin perder recall

IA

Qué va a pasar con el desarrollo de software: predicciones con fechas (julio 2026)