Maquetación CSS moderna: container queries y subgrid
Guía práctica de diseños responsivos con container queries y subgrid: respuesta por componente, herencia de alineación, cuadrículas y paneles.

Más allá de las media queries: capacidad de respuesta a nivel de componente
Las media queries responden al viewport. Las container queries responden al contenedor padre. Esta diferencia transforma la forma en que construimos componentes reutilizables. Un componente de tarjeta que se adapta a su contenedor funciona correctamente tanto si se coloca en una barra lateral estrecha, como en un diseño de dos columnas o en una sección hero a ancho completo, sin necesidad de modificar su CSS para cada contexto.
Las container queries y subgrid son las dos características de maquetación CSS más impactantes desde la propia llegada de Grid. Juntas resuelven problemas que han atormentado al diseño responsivo durante más de una década.
Fundamentos de las container queries
Las container queries requieren declarar un contexto de contención en un elemento padre y, después, escribir consultas contra las dimensiones de ese contenedor.
/* ❌ Media queries — component responds to viewport, not context */
.card {
display: flex;
flex-direction: column;
}
@media (min-width: 768px) {
.card {
flex-direction: row;
}
}
/* Problem: this card in a 300px sidebar still goes horizontal
on a 768px viewport — wrong layout for its context */
/* ✅ Container queries — component responds to its container */
.card-container {
container-type: inline-size;
container-name: card-wrapper;
}
.card {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
}
@container card-wrapper (min-width: 400px) {
.card {
flex-direction: row;
align-items: center;
}
.card__image {
flex: 0 0 40%;
max-width: 200px;
}
.card__content {
flex: 1;
}
}
@container card-wrapper (min-width: 600px) {
.card {
padding: 1.5rem;
gap: 1.5rem;
}
.card__title {
font-size: 1.5rem;
}
}Ahora la tarjeta se adapta a cualquier contenedor en el que se coloque, sin importar el tamaño del viewport. Colócala en una barra lateral estrecha y se apilará verticalmente. Colócala en un área de contenido ancha y pasará a disposición horizontal. No hacen falta media queries.
Unidades de container query
Las unidades de container query (cqi, cqb, cqw, cqh) permiten dimensionar elementos en relación con las dimensiones del contenedor, no del viewport.
.card-container {
container-type: inline-size;
}
.card__title {
/* Font size scales with container width */
font-size: clamp(1rem, 3cqi, 1.5rem);
}
.card__description {
font-size: clamp(0.875rem, 2cqi, 1.125rem);
line-height: 1.6;
}
.card__image {
/* Image height proportional to container */
height: clamp(120px, 30cqi, 250px);
object-fit: cover;
border-radius: 0.5rem;
}<!-- The same component works in any layout context -->
<div class="grid" style="grid-template-columns: 1fr 2fr 1fr;">
<div class="card-container">
<!-- Narrow: stacked, small text -->
<article class="card">
<img class="card__image" src="..." alt="..." />
<div class="card__content">
<h3 class="card__title">Article Title</h3>
<p class="card__description">Description text...</p>
</div>
</article>
</div>
<div class="card-container">
<!-- Wide: horizontal, larger text -->
<article class="card">
<img class="card__image" src="..." alt="..." />
<div class="card__content">
<h3 class="card__title">Article Title</h3>
<p class="card__description">Description text...</p>
</div>
</article>
</div>
<div class="card-container">
<!-- Narrow: stacked, small text -->
<article class="card">
<img class="card__image" src="..." alt="..." />
<div class="card__content">
<h3 class="card__title">Article Title</h3>
<p class="card__description">Description text...</p>
</div>
</article>
</div>
</div>Subgrid: herencia de alineación
Subgrid resuelve el problema de alineación de cuadrículas de tarjetas que ha atormentado a los desarrolladores frontend durante años. Cuando las tarjetas tienen distintas cantidades de contenido, sus elementos internos (título, descripción, pie) quedan desalineados a lo largo de la fila. Subgrid permite que los elementos hijos hereden las pistas (tracks) de la cuadrícula padre.
/* ❌ Without subgrid — card internals don't align across rows */
.product-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
.product-card {
display: flex;
flex-direction: column;
}
/* Titles, descriptions, and prices at different heights
depending on content length. Looks messy. */
/* ✅ With subgrid — card internals align perfectly */
.product-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
.product-card {
display: grid;
grid-row: span 4; /* image, title, description, price */
grid-template-rows: subgrid;
gap: 0.75rem;
border-radius: 0.75rem;
overflow: hidden;
}
.product-card__image {
aspect-ratio: 16 / 10;
object-fit: cover;
width: 100%;
}
.product-card__title {
font-weight: 600;
padding: 0 1rem;
}
.product-card__description {
color: #666;
font-size: 0.9rem;
padding: 0 1rem;
}
.product-card__price {
font-weight: 700;
font-size: 1.25rem;
padding: 0 1rem 1rem;
align-self: end;
}Cada tarjeta de la fila comparte las mismas pistas de fila. Si una tarjeta tiene un título más largo, todas las tarjetas de esa fila asignan el mismo espacio para los títulos. Las descripciones se alinean. Los precios se alinean. La cuadrícula lo gestiona automáticamente.
Maquetación de dashboards con container queries
Los dashboards son el caso de uso ideal para las container queries. Los widgets necesitan adaptarse al espacio que se les asigna, el cual cambia dinámicamente a medida que el usuario redimensiona paneles o colapsa barras laterales.
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1rem;
padding: 1rem;
}
.widget {
container-type: inline-size;
container-name: widget;
background: white;
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Compact widget — shows summary only */
@container widget (max-width: 350px) {
.widget__chart {
display: none;
}
.widget__header {
flex-direction: column;
gap: 0.5rem;
}
.widget__value {
font-size: 1.5rem;
}
}
/* Standard widget — shows chart and summary */
@container widget (min-width: 351px) and (max-width: 600px) {
.widget__chart {
height: 150px;
}
.widget__value {
font-size: 2rem;
}
}
/* Expanded widget — full detail view */
@container widget (min-width: 601px) {
.widget__chart {
height: 250px;
}
.widget__details {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.widget__value {
font-size: 2.5rem;
}
}Cómo combinar container queries con subgrid
El verdadero potencial surge cuando container queries y subgrid trabajan juntas. Las container queries se encargan de la adaptación del diseño; subgrid se encarga de la alineación.
.feature-section {
container-type: inline-size;
container-name: features;
}
.feature-grid {
display: grid;
gap: 2rem;
}
/* Single column for narrow containers */
@container features (max-width: 500px) {
.feature-grid {
grid-template-columns: 1fr;
}
}
/* Two columns with subgrid alignment */
@container features (min-width: 501px) and (max-width: 900px) {
.feature-grid {
grid-template-columns: repeat(2, 1fr);
}
.feature-item {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
}
}
/* Three columns for wide containers */
@container features (min-width: 901px) {
.feature-grid {
grid-template-columns: repeat(3, 1fr);
}
.feature-item {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
}
}
.feature-item__icon {
font-size: 2rem;
}
.feature-item__title {
font-size: clamp(1rem, 2.5cqi, 1.25rem);
font-weight: 600;
}
.feature-item__description {
color: #555;
font-size: clamp(0.875rem, 2cqi, 1rem);
align-self: start;
}Style queries para estilos basados en estado
Las style queries de contenedor permiten responder a los valores de propiedades personalizadas de un contenedor, lo que posibilita estilos de componentes basados en estado sin necesidad de alternar clases mediante JavaScript.
.theme-section {
container-type: inline-size;
--theme: light;
}
.theme-section[data-theme="dark"] {
--theme: dark;
}
/* Style query responds to custom property */
@container style(--theme: dark) {
.card {
background: #1a1a2e;
color: #eee;
border-color: #333;
}
.card__title {
color: #fff;
}
.card__description {
color: #ccc;
}
}
@container style(--theme: light) {
.card {
background: #fff;
color: #333;
border-color: #e0e0e0;
}
}Conclusiones clave
Container queries y subgrid cambian de forma fundamental nuestra manera de abordar el diseño responsivo. Las container queries trasladan la capacidad de respuesta del viewport al nivel del componente, haciendo que los componentes sean verdaderamente reutilizables en distintos contextos de maquetación. Las unidades de container query (cqi) permiten tipografía y dimensionado fluidos en relación con el contenedor del componente.
Subgrid resuelve el problema de alineación de tarjetas permitiendo que los elementos hijos hereden las pistas de la cuadrícula padre. Se acabaron los títulos, descripciones y precios desalineados entre filas de tarjetas. Combinado con container queries, se obtienen componentes que adaptan su diseño al espacio disponible y, al mismo tiempo, alinean sus elementos internos con los de sus hermanos.
No se trata de funcionalidades experimentales: cuentan con amplio soporte en los navegadores y están listas para producción. Todo componente nuevo que construyas debería partir de container queries en lugar de media queries.


