Container Queries: Component-Level Responsiveness
Container queries are the biggest shift in responsive CSS since media queries. Instead of responding to the viewport width, they respond to the size of a parent element — making truly reusable, context-aware components possible.
1. The Problem They Solve
With media queries, a component like .card has no idea whether it's in a narrow sidebar or a wide main column. Container queries let it adapt to its actual container:
Without container queries: With container queries:
┌──────────────────────────────┐ ┌────────────────────────────────────┐
│ Sidebar │ Main Content │ │ Sidebar │ Main Content │
│ .card │ .card │ │ ┌────────┐ │ ┌─────────┐ │
│ ┌──────┐ │ ┌──────────┐ │ │ │Image │ │ │Image │ │
│ │IMG │ │ │IMG │ │ │ │──────-│ │ │ │ │
│ │The │ │ │ │ │ │ │Title │ │ │─────────│ │
│ │title │ │ │Title │ │ │ └────────┘ │ │Title │ │
│ │long │ │ │ │ │ │ (narrow) │ │ │ │
│ └──────┘ │ └──────────┘ │ │ │ └─────────┘ │
│ (same layout regardless) │ │ (adapts to container width!) │
└──────────────────────────────┘ └────────────────────────────────────┘
2. Enabling Container Queries
Step 1: Define a containment context on the parent element:
/* The container — this is the element the query measures */
.card-wrapper {
container-type: inline-size; /* Measure horizontal size */
container-name: card; /* Optional: name it for specificity */
}
Step 2: Write your @container rule:
/* Default: narrow/stacked layout */
.card {
display: flex;
flex-direction: column;
}
/* When the container is wide enough: horizontal layout */
@container card (min-width: 400px) {
.card {
flex-direction: row;
}
.card__image {
width: 200px;
flex-shrink: 0;
}
}
3. container-type Values
| Value | What It Measures |
|---|---|
inline-size | Width only — most common, safest |
block-size | Height only — use rarely |
size | Both width and height |
normal | (Default) No containment — required to reset |
[!NOTE] Use
inline-sizeinstead ofsizeunless you specifically need height-based queries —sizehas stricter performance implications and requires the element have an explicit height.
4. container-name — Multiple Containers
Name your containers to target specific ones in nested structures:
.sidebar { container-type: inline-size; container-name: sidebar; }
.main-content { container-type: inline-size; container-name: main; }
.card-list { container-type: inline-size; container-name: card-list; }
/* Target specific containers */
@container sidebar (max-width: 300px) {
.nav-item { flex-direction: column; }
}
@container main (min-width: 800px) {
.post-grid { grid-template-columns: repeat(3, 1fr); }
}
5. Container Query Units
cq* units are relative to the container — similar to vw/vh but for the container:
| Unit | Relative To |
|---|---|
cqw | 1% of container width |
cqh | 1% of container height |
cqi | 1% of container inline size |
cqb | 1% of container block size |
cqmin | Smaller of cqi or cqb |
cqmax | Larger of cqi or cqb |
/* Font scales with the container, not the viewport */
@container (min-width: 300px) {
.card__title { font-size: clamp(1rem, 5cqi, 2rem); }
}
6. Real World: Adaptive Card Component
<div class="card-wrapper">
<article class="card">
<div class="card__media">
<img src="..." alt="...">
</div>
<div class="card__body">
<h2 class="card__title">Title</h2>
<p class="card__excerpt">Excerpt text...</p>
</div>
</article>
</div>
/* Container context */
.card-wrapper { container-type: inline-size; container-name: card; }
/* Default: vertical stack (narrow) */
.card { display: flex; flex-direction: column; gap: 0; }
.card__media img { width: 100%; aspect-ratio: 16/9; object-fit: cover; }
/* Medium card: horizontal layout */
@container card (min-width: 380px) {
.card { flex-direction: row; }
.card__media { width: 140px; flex-shrink: 0; }
.card__media img { height: 100%; aspect-ratio: auto; }
}
/* Wide card: extra info, larger font */
@container card (min-width: 600px) {
.card__title { font-size: 1.5rem; }
.card__excerpt { display: block; } /* Was hidden in narrow layout */
.card__meta { display: flex; } /* Show date/author row */
}
7. Real World: Responsive Grid Without Breakpoints
.post-grid { container-type: inline-size; }
.post-grid .posts {
display: grid;
gap: 1.5rem;
grid-template-columns: 1fr; /* 1 col default */
}
@container (min-width: 500px) {
.post-grid .posts { grid-template-columns: repeat(2, 1fr); }
}
@container (min-width: 800px) {
.post-grid .posts { grid-template-columns: repeat(3, 1fr); }
}
Now this grid adapts whether it's in a sidebar, a tabs panel, or full-width — no more viewport breakpoints needed.
8. Container Queries vs Media Queries
| Use Case | Use |
|---|---|
| Global layout (sidebar on/off) | Media Query |
| Component adapts to its context | Container Query |
| Typography based on viewport | Media Query or clamp() |
| Card layout in variable columns | Container Query |
| Print styles | Media Query |
| Dark mode preference | Media Query |
[!TIP] The rule of thumb: If you're styling a page structure, use media queries. If you're styling a component, use container queries. Most components benefit from container queries over media queries.
9. WordPress: Container Queries in GeneratePress
/* Wrap the widget area with a container context */
.widget-area { container-type: inline-size; container-name: widget-area; }
@container widget-area (max-width: 300px) {
.widget { padding: 1rem; }
.widget-title { font-size: 1rem; }
}
/* Bricks Builder container query */
.brxe-container { container-type: inline-size; }
@container (min-width: 500px) {
.brxe-block { flex-direction: row; }
}