Episode 1 — Fundamentals / 1.8 — CSS Layout Mastery
1.8.g — Container Queries
In one sentence: Container queries let a component adapt its styles based on its container's size instead of the viewport — making truly reusable, context-aware components possible in pure CSS.
Navigation: ← 1.8.f — Stacking Context · 1.8 Overview
1. The problem container queries solve
Media queries respond to the viewport width:
@media (max-width: 600px) {
.card { flex-direction: column; }
}
But a card component does not care about the viewport — it cares about how much space it actually has. A card in a narrow sidebar should look different from the same card in a wide main area, regardless of viewport width.
Viewport: 1200px
┌──────────┬──────────────────────────────────┐
│ Sidebar │ Main content │
│ (300px) │ (900px) │
│ │ │
│ ┌──────┐ │ ┌─────────────────────────────┐ │
│ │ Card │ │ │ Card │ │
│ │ (col)│ │ │ (row layout) │ │
│ └──────┘ │ └─────────────────────────────┘ │
└──────────┴──────────────────────────────────┘
Same component, same viewport — different layout based on CONTAINER width.
2. @container syntax
Step 1: Define a container
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
Step 2: Query the container
@container sidebar (max-width: 400px) {
.card {
flex-direction: column;
}
}
@container sidebar (min-width: 401px) {
.card {
flex-direction: row;
}
}
If you omit container-name, the query matches the nearest ancestor with container-type set.
@container (min-width: 500px) {
.card { /* matches nearest container ancestor */ }
}
3. Container types
container-type | What it enables | Containment |
|---|---|---|
inline-size | Queries on width (inline dimension) | Size containment on inline axis |
size | Queries on width and height | Size containment on both axes |
normal (default) | No container queries | No containment |
inline-size is the most common — you almost always want to query width, not height.
container shorthand
.panel {
container: panel / inline-size;
/* equivalent to: container-name: panel; container-type: inline-size; */
}
4. Container query units
Container queries come with relative units based on the container's dimensions:
| Unit | Relative to |
|---|---|
cqw | 1% of container's width |
cqh | 1% of container's height |
cqi | 1% of container's inline size |
cqb | 1% of container's block size |
cqmin | Smaller of cqi and cqb |
cqmax | Larger of cqi and cqb |
@container (min-width: 400px) {
.card-title {
font-size: clamp(1rem, 3cqi, 1.5rem);
}
}
These units let typography and spacing scale with the component's container, not the viewport.
5. Responsive components vs responsive pages
| Approach | Responds to | Best for |
|---|---|---|
Media queries (@media) | Viewport | Page-level layout, global breakpoints |
Container queries (@container) | Container | Reusable components, design system widgets |
The two are complementary:
- Use media queries for the page skeleton (grid columns, mobile nav toggle).
- Use container queries for components that appear in multiple contexts (cards, widgets, data tables).
6. Browser support
Container queries are supported in all modern browsers (Chrome 105+, Firefox 110+, Safari 16+). For production use, this covers the vast majority of users as of 2025/2026.
Fallback strategy: Write the mobile/small layout as the default, and enhance with @container for wider containers. Browsers that do not support container queries simply see the default layout.
/* Default: stacked layout */
.card {
display: flex;
flex-direction: column;
}
/* Enhanced: row layout when container is wide enough */
@container (min-width: 500px) {
.card {
flex-direction: row;
}
}
7. Practical examples
Card that adapts to its container width
.card-wrapper {
container-type: inline-size;
}
.card {
display: flex;
flex-direction: column;
gap: 12px;
}
.card img {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
@container (min-width: 500px) {
.card {
flex-direction: row;
}
.card img {
width: 200px;
aspect-ratio: 1;
}
}
@container (min-width: 800px) {
.card img {
width: 300px;
}
.card h2 {
font-size: 1.5rem;
}
}
Sidebar widget that simplifies at small size
.widget-container {
container: widget / inline-size;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
@container widget (max-width: 300px) {
.stats-grid {
grid-template-columns: 1fr; /* stack when narrow */
}
.stat-label {
display: none; /* show only the number in tight spaces */
}
}
Responsive navigation within a component
.nav-container {
container-type: inline-size;
}
.nav-links {
display: flex;
gap: 16px;
}
@container (max-width: 400px) {
.nav-links {
flex-direction: column;
gap: 8px;
}
}
8. Key takeaways
- Container queries make components responsive to their container, not the viewport.
- Set
container-type: inline-sizeon the container, then query with@container. - Use
cqi/cqwunits for container-relative sizing. - Media queries handle page-level layout; container queries handle component-level responsiveness.
- Browser support is solid — write mobile-first defaults and enhance with
@container.
Explain-It Challenge
Explain without notes:
- Why can't media queries solve the problem of a card that appears in both a sidebar and a main content area?
- What is the difference between
container-type: inline-sizeandcontainer-type: size? - How would you provide a fallback for browsers that do not support container queries?
Navigation: ← 1.8.f — Stacking Context · 1.8 Overview