Container queries are one of the most significant additions to CSS in recent years. They let you style elements based on the size of their container rather than the viewport. This changes how you think about responsive components.
The Problem with Media Queries
Media queries respond to the viewport width. This works for page-level layouts, but breaks down for reusable components:
/* This card needs to be compact in a sidebar but wide in the main area */
@media (min-width: 768px) {
.card { display: flex; }
}The media query does not know where the card lives. If the card is in a narrow sidebar on a wide screen, it still gets the wide layout. Media queries answer "how wide is the screen?" but components need to ask "how wide is my container?"
Container Query Syntax
To use container queries, you need two things: a containment context and a container query.
Step 1: Define the Container
.card-wrapper {
container-type: inline-size;
container-name: card;
}The container-type: inline-size property establishes the element as a query container for its inline dimension (width in horizontal writing modes). The container-name is optional but helps with clarity when you have multiple containers.
Shorthand syntax combines both:
.card-wrapper {
container: card / inline-size;
}Step 2: Write the Query
@container card (min-width: 400px) {
.card {
display: flex;
gap: 1.5rem;
}
.card-image {
width: 200px;
flex-shrink: 0;
}
}
@container card (min-width: 600px) {
.card {
padding: 2rem;
}
.card-title {
font-size: 1.5rem;
}
}Now the card adapts based on how much space its wrapper has — whether that is in a sidebar, a grid cell, or a full-width section.
A Complete Example
<div class="grid">
<div class="card-wrapper">
<article class="card">
<img class="card-image" src="/photo.jpg" alt="Project screenshot" />
<div class="card-content">
<h3 class="card-title">Project Update</h3>
<p>The dashboard redesign is complete.</p>
</div>
</article>
</div>
</div>.card-wrapper {
container: card / inline-size;
}
/* Default: stacked layout */
.card {
display: grid;
gap: 1rem;
}
.card-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: 0.5rem;
}
/* When container is wide enough: horizontal layout */
@container card (min-width: 500px) {
.card {
grid-template-columns: 200px 1fr;
align-items: start;
}
.card-image {
aspect-ratio: 1;
}
}Place this card in a single-column layout and it stacks. Place it in a three-column grid and it stacks. Place it in a two-column grid with wide columns and it goes horizontal. The component itself decides — no external media queries needed.
Container vs Media Queries
| Feature | Media Queries | Container Queries |
|---|---|---|
| Responds to | Viewport size | Parent container size |
| Best for | Page layouts | Reusable components |
| Nesting | No concept of nesting | Queries nearest container ancestor |
| Browser support | Universal | Modern browsers (Chrome 105+, Firefox 110+, Safari 16+) |
Use media queries for page-level layout decisions: how many columns in the grid, when to show the sidebar, when to switch navigation. Use container queries for component-level decisions: card layout, widget arrangement, content density.
Container Query Units
Container queries also introduce new units relative to the container size:
| Unit | Meaning |
|---|---|
cqw | 1% of the container's width |
cqh | 1% of the container's height |
cqi | 1% of the container's inline size |
cqb | 1% of the container's block size |
@container (min-width: 300px) {
.card-title {
font-size: clamp(1rem, 4cqi, 1.75rem);
}
}This makes typography fluid relative to the container, not the viewport.
Practical Tips
- Always use
inline-size, notsize. Thesizecontainment type restricts both axes and prevents the element from sizing based on its content height, which often breaks layouts. - Name your containers to avoid ambiguity when nesting multiple containers.
- Combine with media queries for the best results. Use media queries for the overall page structure and container queries for components within that structure.
- Provide a sensible default outside the
@containerblock. The base styles should work even in the smallest container.