Durante años hemos diseñado componentes responsivos basándonos en el ancho del viewport. El problema es que un componente de card puede vivir en una sidebar angosta, en un grid de tres columnas o en un layout de pantalla completa. El viewport no te dice nada útil sobre cuánto espacio tiene ese componente específico. Container Queries sí.
El problema de los media queries para componentes
Imagina un componente <ProductCard> que usas en tres lugares distintos:
Sidebar (280px) | Grid 3col (360px) | Pantalla completa (800px)
Con media queries, tienes que saber desde afuera en qué contexto vive el componente para aplicarle los estilos correctos. Esto crea acoplamiento: el componente no puede ser verdaderamente reutilizable porque sus breakpoints están atados al layout global.
Container Queries rompen ese acoplamiento. El componente se adapta a su propio contenedor, sin importar dónde esté en la página.
Sintaxis básica
Primero defines el contenedor, luego escribes queries relativas a él:
/* Paso 1: Define el contenedor */
.card-wrapper {
container-type: inline-size;
container-name: card; /* opcional pero recomendado */
}
/* Paso 2: Escribe queries relativas al contenedor */
.card {
display: flex;
flex-direction: column;
gap: 12px;
}
@container card (min-width: 400px) {
.card {
flex-direction: row;
align-items: center;
}
.card__image {
width: 160px;
flex-shrink: 0;
}
}
@container card (min-width: 600px) {
.card {
gap: 24px;
}
.card__title {
font-size: 1.5rem;
}
}
Ahora el componente se adapta automáticamente sin importar si está en una sidebar de 280px o en un layout de 800px.
Container Query Units: el poder oculto
Además de los queries, CSS introduce unidades relativas al contenedor:
.card__title {
/* cqi = container query inline (como vw pero para el contenedor) */
font-size: clamp(14px, 4cqi, 24px);
}
.card__hero-image {
/* 100% del ancho del contenedor */
width: 100cqi;
}
cqi se comporta como vw pero referenciado al contenedor más cercano con container-type. Esto abre posibilidades de tipografía fluida completamente independiente del viewport.
Patrón avanzado: componentes con múltiples estados de layout
El patrón que más uso en producción es el “layout shifting component”:
.article-card-container {
container-type: inline-size;
container-name: article-card;
}
/* Mobile-first: layout compacto */
.article-card {
display: grid;
grid-template-areas:
'image'
'meta'
'title'
'excerpt';
gap: 12px;
}
/* Componente mediano: imagen a la izquierda */
@container article-card (min-width: 320px) {
.article-card {
grid-template-columns: 120px 1fr;
grid-template-areas:
'image meta'
'image title'
'image excerpt';
}
}
/* Componente grande: layout editorial horizontal */
@container article-card (min-width: 560px) {
.article-card {
grid-template-columns: 240px 1fr;
grid-template-rows: auto auto 1fr;
grid-template-areas:
'image meta'
'image title'
'image excerpt';
gap: 24px;
}
.article-card__title {
font-size: clamp(18px, 3cqi, 28px);
}
}
Un solo componente, tres layouts, cero conocimiento del contexto global.
Soporte y cuándo usarlo
Container Queries tienen soporte del 93%+ en navegadores modernos (Chrome 105+, Firefox 110+, Safari 16+). Para proyectos que no soportan IE o versiones muy antiguas, puedes usarlos sin polyfills hoy mismo.
El patrón de adopción que recomiendo: úsalos para todos los componentes nuevos y migra los existentes gradualmente cuando toques ese código.
El cambio de mentalidad
El salto conceptual más importante: deja de pensar en “cómo se ve este componente en móvil vs desktop” y empieza a pensar en “cómo se ve este componente cuando tiene poco espacio vs mucho espacio”. Son preguntas distintas con respuestas distintas.
Container Queries hacen tus componentes realmente encapsulados. Ese es su poder real.