Media queries ask one question: how wide is the screen? For whole-page layouts that's fine. But modern sites are built from reusable components, such as product cards, profile panels and widgets, and the same card might appear in a wide main column, a narrow sidebar and a three-column grid on the same page. The screen width tells you nothing about how much room that particular card has.
Container queries answer a better question: how wide is the box this component is inside? A card can switch from a stacked layout to a side-by-side layout when its container is wide enough, wherever you put it. You write the responsive behaviour once, inside the component, and it just works in every context.
Container queries are supported in all modern browsers. In this lesson you'll create containers with container-type, write @container rules, name containers when you nest them, and size things with container query units such as cqi. You'll also see when media queries are still the right tool.
Step 1: Make a container
.card-wrap { container-type: inline-size; }container-type: inline-sizetells the browser to track this element's width (its inline size in left-to-right languages), so its descendants can query it.- The container itself can't be styled by its own query; only its children respond.
container-type: sizetracks height too, but then the element must get its height from somewhere other than its content, soinline-sizeis what you'll use almost every time.
Step 2: Write an @container rule
.card { display: grid; gap: 12px; }
@container (min-width: 420px) {
.card { grid-template-columns: 160px 1fr; }
}The syntax looks just like a media query. Inside, the conditions refer to the nearest ancestor container. Mobile-first thinking works here too: write the narrow version as the default, then add a wider version inside @container (min-width: ...). Newer syntax also accepts ranges such as @container (width > 420px).
Named containers
When containers are nested, a query uses the closest one. To target a specific ancestor, name it:
.sidebar { container: sidebar / inline-size; } /* shorthand: name / type */
@container sidebar (min-width: 300px) {
.widget { padding: 24px; }
}The container shorthand sets container-name and container-type together.
Container query units
| Unit | Equals |
|---|---|
cqi | 1% of the container's inline size (width) |
cqb | 1% of the container's block size (height) |
cqw / cqh | 1% of the container's width / height |
cqmin / cqmax | The smaller / larger of cqi and cqb |
They work like viewport units but relative to the container, so font-size: clamp(1rem, 4cqi, 1.75rem) makes a card title scale with the card, not the screen.
CSS Container Queries Syntax
Container queries
.wrap { container-type: inline-size; }
.sidebar { container: sidebar / inline-size; }
@container (min-width: 400px) { .card { display: flex; } }
@container sidebar (width < 300px) { .card { font-size: 0.9rem; } }
.title { font-size: clamp(1rem, 5cqi, 2rem); }CSS Container Queries Summary
- Container queries let components respond to their container's size, not the screen's.
- Create a container with
container-type: inline-size. - Write
@container (min-width: ...)rules for the children. - Name containers with
container: name / inline-sizeto target a specific ancestor. cqiunits size things relative to the container's width.
CSS Container Queries Examples
Each example runs in the page. Edit the code and press Run to see what changes.
One card, two containers
The same card HTML and CSS appears in a narrow and a wide container.
<div class="layout">
<div class="narrow wrap">
<article class="card"><img src="https://picsum.photos/seed/cq1/400/300" alt="Coffee beans" width="400" height="300"><div><h3>Single origin beans</h3><p>Roasted every Monday.</p></div></article>
</div>
<div class="wide wrap">
<article class="card"><img src="https://picsum.photos/seed/cq1/400/300" alt="Coffee beans" width="400" height="300"><div><h3>Single origin beans</h3><p>Roasted every Monday.</p></div></article>
</div>
</div>.layout { display: grid; grid-template-columns: 200px 1fr; gap: 16px; font-family: sans-serif; }
.wrap { container-type: inline-size; }
.card { display: grid; gap: 10px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 10px; }
.card img { width: 100%; height: auto; border-radius: 8px; }
.card h3 { margin: 0; }
@container (min-width: 380px) {
.card { grid-template-columns: 140px 1fr; align-items: center; }
}Line-by-line explanation
- Line 3Both wrappers become containers.
- Line 5By default the card stacks the image above the text.
- Lines 9 to 11When its container is at least 380px wide, the card goes side by side. Only the wide copy changes.
Container query units
Drag the panel narrower. The title shrinks with its box.
<div class="box">
<h2 class="title">Summer sale</h2>
<p>Up to half price on garden furniture.</p>
</div>.box {
container-type: inline-size;
width: 60%;
min-width: 200px;
padding: 16px;
background: #fef3c7;
border-radius: 12px;
font-family: sans-serif;
}
.title {
font-size: clamp(1.25rem, 9cqi, 3rem);
margin: 0 0 6px;
}Line-by-line explanation
- Line 2The box is the container.
- Line 3Its width depends on the panel, so it changes as you resize.
- Line 12The title is about 9% of the box's width, between 20px and 48px.
Named containers in a dashboard
<div class="dash">
<main class="main"><div class="stat"><span class="num">1,284</span><span>Orders</span></div></main>
<aside class="side"><div class="stat"><span class="num">92%</span><span>On time</span></div></aside>
</div>.dash { display: grid; grid-template-columns: 2fr 1fr; gap: 12px; font-family: sans-serif; }
.main { container: main / inline-size; }
.side { container: side / inline-size; }
.stat { display: grid; gap: 4px; padding: 14px; background: #eef2ff; border-radius: 10px; }
.num { font-size: 1.5rem; font-weight: 700; }
@container main (min-width: 300px) {
.stat { grid-template-columns: auto 1fr; align-items: baseline; gap: 12px; }
.num { font-size: 2.25rem; }
}Line-by-line explanation
- Lines 2 to 3Two named containers.
- Line 8This rule only listens to the
maincontainer. - Lines 9 to 10Stats inside main become larger and side by side; the sidebar stat stays compact.
Common CSS Container Queries Mistakes
Querying the container itself
.card { container-type: inline-size; }
@container (min-width: 400px) { .card { display: flex; } } /* .card can't query itself */.card-wrap { container-type: inline-size; }
@container (min-width: 400px) { .card { display: flex; } }Using container-type: size by accident
.wrap { container-type: size; } /* the box collapses to zero height */.wrap { container-type: inline-size; }With size, the container can't take its height from its content.
Forgetting to create a container
@container (min-width: 400px) { .card { display: flex; } } /* no ancestor has container-type */.grid > * { container-type: inline-size; }
@container (min-width: 400px) { .card { display: flex; } }Without a container ancestor, the query never matches.
CSS Container Queries in Real Projects
Container queries in practice
Design systems use container queries so a single card component works in grids, sidebars and modals. Content management systems benefit most, because editors can drop a component into any column. Shopify themes, news sites and dashboards adopted them soon after browsers shipped support in 2023. Combined with cqi units, typography inside components scales naturally.
CSS Container Queries Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Make the wrapper a container that tracks its width.
.wrap {
container-type: ;
}
inline-size tracks the width only.What does a container query measure?
What does 10cqi equal?
When are media queries still the better choice?
The card should go side by side when its wrapper is at least 300px wide. Fix the container and the query.
<div class="w" style="width: 400px"><div class="c"><div>Img</div><div>Text</div></div></div>.c { container-type: size; display: grid; }
@media (min-width: 300px) {
.c { grid-template-columns: 1fr 1fr; }
}- Wrapper is an inline-size container
- Uses @container
- Card is side by side
CSS Container Queries Quiz
5 questions to check what you learned.
Which property creates a container?
- display: container
- @container
- position: container
- container-type
Why are container queries useful for reusable components?
- They're faster than media queries
- They replace flexbox
- The same component adapts to wherever it's placed
- They work without CSS
What does container: card / inline-size do?
- Names the container card and sets its type to inline-size
- Creates a class called card
- Sets the width to inline
- Imports a component
With nested containers, which one does an unnamed @container query use?
- The outermost one
- The nearest ancestor container
- The body
- All of them
Why does container-type: size often cause boxes to collapse?
- It's a bug
- It sets display: none
- It removes padding
- The container can no longer get its height from its content
CSS Container Queries Challenge
A responsive profile widget
- Make
.slotaninline-sizecontainer namedslot - By default,
.profileisdisplay: gridwith agapof8px - In
@container slot (min-width: 350px), give.profilegrid-template-columns: 80px 1fr - Give
.nameafont-sizeofclamp(1rem, 6cqi, 2rem)
<div class="slot" id="big" style="width: 500px"><div class="profile"><div class="pic"></div><p class="name">Jo Park</p></div></div>
<div class="slot" id="small" style="width: 220px"><div class="profile"><div class="pic"></div><p class="name">Jo Park</p></div></div>.pic { height: 80px; background: #c7d2fe; border-radius: 8px; }
- Named inline-size container
- Profile is a grid with an 8px gap
- Wide slot goes side by side
- Narrow slot stays stacked
- Name scales with its container