Container Queries
Components That Respond to Their Own Size
Think about this: media queries respond to the viewport. But a card component can live in a full-width main area or a narrow sidebar -- the viewport is the same, but the card's available space is completely different. Container queries let components respond to their container's size, not the viewport. This is the missing piece for truly reusable responsive components.
Think of container queries as thermometers on each room, not just one for the building. Media queries are like a single building thermostat — every room gets the same temperature adjustment. Container queries give each room its own thermostat. A card in the sidebar adjusts to sidebar width. The same card in the main content adjusts to main content width. The component is self-aware.
Setting Up Containment
A container query requires two things: a container context on the parent and a @container rule on the children.
/* Step 1: Declare a container */
.card-wrapper {
container-type: inline-size; /* Enable width-based container queries */
}
/* Step 2: Query the container */
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
@container (min-width: 600px) {
.card {
grid-template-columns: 300px 1fr;
gap: 2rem;
}
}
container-type Values
.a { container-type: inline-size; } /* Query width only (most common) */
.b { container-type: size; } /* Query both width and height */
.c { container-type: normal; } /* Default — no containment */
container-type: inline-size establishes size containment on the inline axis. This means the container's width can no longer depend on its children's widths — it breaks the intrinsic sizing feedback loop. If you set container-type: inline-size on an element without explicit width, it may collapse to zero width if it would normally be sized by content.
Named Containers
When containers are nested, you need names to target the right one:
.sidebar {
container-name: sidebar;
container-type: inline-size;
}
.main {
container-name: main-content;
container-type: inline-size;
}
/* Shorthand */
.sidebar { container: sidebar / inline-size; }
/* Query a specific container by name */
@container sidebar (min-width: 300px) {
.widget { display: grid; grid-template-columns: 1fr 1fr; }
}
@container main-content (min-width: 800px) {
.article { columns: 2; }
}
Without a name, @container queries the nearest ancestor with container-type set.
Container Query Units
It gets better. Container queries also provide new units relative to the container's size:
.card-wrapper { container-type: inline-size; }
.card-title {
font-size: clamp(1rem, 3cqi, 1.5rem); /* cqi = 1% of container inline size */
}
.card-image {
height: 30cqb; /* cqb = 1% of container block size */
/* Requires container-type: size (not just inline-size) */
}
| Unit | Meaning |
|---|---|
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/cqb |
cqmax | Larger of cqi/cqb |
Production Scenario: Reusable Card Component
/* The card container — wherever it lives */
.card-container {
container: card / inline-size;
}
/* Default: vertical stack (narrow context — sidebar, mobile) */
.card {
display: flex;
flex-direction: column;
gap: 1rem;
}
.card-image {
aspect-ratio: 16 / 9;
border-radius: 8px;
overflow: hidden;
}
.card-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Medium: horizontal layout */
@container card (min-width: 400px) {
.card {
flex-direction: row;
align-items: flex-start;
}
.card-image {
flex: 0 0 180px;
aspect-ratio: 1;
}
}
/* Large: rich layout with metadata row */
@container card (min-width: 600px) {
.card-image {
flex: 0 0 240px;
}
.card-meta {
display: flex;
gap: 1rem;
margin-top: auto;
}
}
This card works in a sidebar (stacks vertically), in a two-column grid (horizontal), and in a full-width hero area (rich layout) -- all without a single media query. The component is self-aware. Drop it anywhere and it just works.
Container Queries vs Media Queries
So when do you use which? Here's the breakdown.
| Feature | Media Queries | Container Queries |
|---|---|---|
| Responds to | Viewport size | Container size |
| Best for | Page-level layout changes | Component-level responsiveness |
| Reusability | Component behavior tied to viewport | Component works anywhere |
| Nesting | N/A | Query specific named containers |
| Browser support | Universal | Modern browsers (2023+) |
Decision framework:
- Page layout (sidebar visibility, nav style): Use media queries
- Component layout (card orientation, widget density): Use container queries
- Sizing (font-size, padding): Use fluid values (clamp) first, container queries for step changes
| What developers do | What they should do |
|---|---|
| Setting container-type on the component itself instead of its wrapper The container must be the element whose size the component should respond to. A component can't query its own size. | Set container-type on the PARENT that provides the available space, not the component being styled |
| Using container-type: size when only width queries are needed container-type: size also constrains height, which can cause elements to collapse if height isn't explicitly set | Use container-type: inline-size unless you specifically need height containment |
| Using container queries for page-level layout changes Container queries are for component reusability. Page-level layout doesn't need to respond to container size. | Use media queries for page-level layout (sidebar, nav). Use container queries for component-level responsiveness. |
| Forgetting that containment prevents intrinsic sizing from content container-type: inline-size means the container ignores its content's width — it must get width from outside | Ensure containers have width from their layout context (grid, flex, explicit width), not from content |
- 1Set container-type on the parent that provides space, not on the component being queried
- 2Use container-type: inline-size for width queries (most common), size for width + height
- 3Name containers when nesting to target the correct ancestor
- 4Container queries are for component-level responsiveness. Media queries are for page-level layout.
- 5Container units (cqi, cqb) let you size things relative to the container, not the viewport