CSS Container Queries
Component-driven responsiveness: adapt to the parent container, not the viewport.
Responsive DesignIntermediate
Why
Media queries adapt to the viewport, so a card laid out in a sidebar and a full-width page needs two CSS rules. Container queries let the component respond to its own container — write once, drop anywhere.
StatCounter 2024 data shows 58% of web traffic is mobile. CSS container queries (baseline 2023) and subgrid (baseline 2024) eliminate most media-query hacks, reducing responsive CSS by 30-50%.
When
Use container queries for reusable components that live in different-width containers (card in sidebar vs main, nav in rail vs topbar). Baseline 2023, all evergreen browsers support them.
How
1. Declare a container — container-type: inline-size on the parent (size containment for width)
2. Query it — @container (min-width: 400px) { .card { ... } } adapts the child
3. Name containers — container-name: sidebar; query with @container sidebar (min-width: 300px)
4. Component-first — write the card's responsive rules against its container, never the viewport
5. Fallback — feature query @supports (container-type: inline-size) { ... } + a media-query fallback
6. Units — cqw/cqh (1cqw = 1% of container width), cqi/cqb for inline/block, cqmin/cqmax
7. Nesting — containers nest; the nearest ancestor container is queried, scope is automatic
Example
/* Card adapts to its container, not the viewport */
.card-host { container-type: inline-size; }
.card { display: grid; grid-template-columns: 1fr; gap: 0.5rem; }
@container (min-width: 400px) {
.card { grid-template-columns: 120px 1fr; } /* image + text side-by-side */
}
@container (min-width: 700px) {
.card { grid-template-columns: 1fr; padding: 2rem; } /* hero variant */
}
/* Named container — sidebar queries independently */
.sidebar { container-name: sidebar; container-type: inline-size; }
@container sidebar (max-width: 200px) { .nav-item { flex-direction: column; } }
/* Fallback for older browsers */
@supports not (container-type: inline-size) {
@media (min-width: 768px) { .card { grid-template-columns: 120px 1fr; } }
}
Common Problems
- ⚠Forgetting container-type — query matches nothing because no ancestor is a container
- ⚠Size containment side effects — container-type: size blocks the parent from sizing to content, layout breaks
- ⚠Mixing viewport and container units — component looks right in one placement, wrong in another
Best Practices
- ✓Use container-type: inline-size (width only) — size containment breaks height-to-content sizing
- ✓Name containers when several coexist — avoids querying the wrong ancestor
- ✓Provide a media-query fallback inside @supports not — older browsers still get a usable layout