CSS Container Queries Explained with Real Examples
Container queries let components style themselves based on their parent's size, not the viewport. Learn the syntax, the units, and when to use them.
What you'll learn
- ✓Why media queries are not enough for components
- ✓How container-type and container-name work
- ✓How to write @container rules
- ✓How container query units like cqw differ from vw
- ✓How to refactor a card to be container-responsive
Prerequisites
- •Basic CSS, including media queries
What and Why
For years, responsive design meant “look at the viewport and adjust.” That works for the page as a whole. It fails for a reusable component dropped into a sidebar one day and a full-width hero the next: the viewport is identical, but the available space is not.
Container queries solve this. A component can ask, “How wide am I right now?” and style itself accordingly, regardless of the viewport. The result: truly portable components.
Mental Model
A container query has two parts: a container declared on some ancestor, and @container rules that react to that container’s size.
Media query: asks the viewport (window)
@media (min-width: 600px) { ... }
Container query: asks the nearest matching container
@container (min-width: 400px) { ... } You opt an element in as a container by setting container-type. The two main values are inline-size (most common; queries can ask about width) and size (both axes; requires the element to have a definite size).
Hands-on Example
A product card that switches from a stacked layout to side-by-side once it has room:
<article class="card">
<img src="/p.jpg" alt="" />
<div class="body">
<h3>Wireless Headphones</h3>
<p>Studio-grade sound, 30 hour battery.</p>
<button>Add to cart</button>
</div>
</article>
.card {
container-type: inline-size;
container-name: card;
display: grid;
gap: 1rem;
padding: 1rem;
background: #fff;
border-radius: 0.75rem;
}
.card img { width: 100%; height: auto; }
@container card (min-width: 420px) {
.card {
grid-template-columns: 160px 1fr;
align-items: start;
}
.card img { width: 160px; }
}
Now drop this card in a 320px sidebar and it stacks; drop it in a 900px main column and it goes side-by-side. The card does not know or care about the viewport.
You can also use the shorthand:
.card { container: card / inline-size; }
Container query units scale to the container, not the viewport. 1cqw is 1% of the container’s width; 1cqh is 1% of its height. Useful for fluid typography inside a component:
.card h3 {
font-size: clamp(1rem, 0.9rem + 1.2cqw, 1.5rem);
}
The heading scales with the card’s width, capped by min and max. A small card gets a smaller title; a wide one gets a bigger one, without media queries.
You can query container styles too (not just size) for advanced theming, though browser support varies. For most projects, @container (min-width: ...) is the workhorse.
Common Pitfalls
Forgetting to set container-type. Without it, @container rules never match. The default is normal, which is opt-out.
Putting container-type: size on an element without explicit dimensions. The query has nothing to measure and the rules do not fire. inline-size is safer because it only constrains the inline axis.
Querying the wrong container. @container matches the nearest ancestor that is a container; if multiple ancestors qualify, name them and use @container card (...) to disambiguate.
Layout shifts due to containment. container-type implies contain: layout (and more for size), which can affect how absolutely positioned children behave. Test that overlays and tooltips still appear in the right place.
Treating container queries as a replacement for media queries. They complement each other: media queries handle page-level decisions (sidebar visibility, font size baseline), container queries handle component-level adaptations.
Practical Tips
Wrap reusable components in a container so they can be dropped anywhere. The wrapping element becomes the public boundary of the component.
Name your containers when a component is nested inside another component that is also a container. Without names you may target the wrong one.
Use cqi (inline size of the container) for fluid type and spacing inside the component. It behaves like a “self-aware vw”.
Test components in at least three container widths: narrow (300px), medium (500px), wide (800px). A storybook-style harness pays for itself the first time you spot a wrap that you missed with the dev tools at 1440px.
Combine container queries with logical properties (padding-inline, margin-block) for layouts that handle both responsive sizes and right-to-left languages cleanly.
Wrap-up
Container queries shift responsive design from the page to the component. Declare a container, write @container rules against its size, and use cq* units for fluid internals. The payoff is components that look right wherever they land, and CSS that stops leaking layout assumptions across files.
Related articles
- HTML & CSS CSS Cascade Layers and Specificity: A Calmer Mental Model
Stop fighting !important. Learn how the CSS cascade, specificity, and the new @layer rule combine to give you predictable, maintainable styles.
- HTML & CSS CSS Clamp and Fluid Typography
Use the CSS clamp function to build fluid typography and spacing that scales smoothly between breakpoints without media queries or jarring jumps.
- HTML & CSS CSS Grid vs Flexbox: When to Use Each (with Examples)
Stop flipping a coin between Grid and Flexbox. Learn the mental model that picks the right layout tool every time, with practical CSS examples.
- HTML & CSS CSS :has() Pseudo-Class Tutorial
Learn how the CSS :has() pseudo-class enables true parent selectors, conditional styling, and sibling-aware rules. Includes a mental model, examples, pitfalls, and best practices for production use.