Container Queries for Reusable Components
Build a tutorial card that responds to the space it lives in, not just the size of the browser window.
CSS The Browser Window Is Not Always the Problem
You have already used media queries. They are useful, familiar, and they have carried responsive design for years.
A media query asks: How wide is the browser window? That is helpful when you are designing a whole page. But a reusable card might appear in a three-column grid, a narrow sidebar, a dashboard panel, or a wide featured area. The browser can be wide while the card itself is squeezed.
A container query asks a better component question: How much space does this component actually have?
- Have you ever had a card look fine on the main page but cramped in a sidebar?
- Which parts of a component should depend on page width, and which should depend on local space?
- What would improve if a component could adapt wherever it was placed?
This lesson follows Modern CSS Layout Extensions. You have already seen responsive grids, minmax(), clamp(), and :has(). Now you will make the component itself respond to its own layout context.
Learning Objectives
By the end of this lesson, you'll be able to:
- ✓ Explain Explain why media queries are not always enough for reusable components
- ✓ Create Create a query container with container-type
- ✓ Use @container to change component styles based on container width
- ✓ Use named containers to target a specific layout context
- ✓ Use container query units such as cqi, cqw, cqmin, and cqmax
- ✓ Build component variants based on available space
- ✓ Decide when to use media queries and when to use container queries
- ✓ Stress-test a reusable component with long content, missing content, and narrow containers
Why This Matters:
Container queries make responsive CSS more local. They let a reusable component adapt to the space around it instead of assuming the viewport tells the whole story.
Before You Start:
You should be familiar with:
- CSS Grid for Repeated Layouts Review here
- Responsive Refinement for Reusable Components Review here
- Modern CSS Layout Extensions Review here
Media Queries Look Outward. Container Queries Look Nearby.
A media query responds to the viewport, device, or user preference. A container query responds to an ancestor container around the component. That small shift changes how reusable CSS feels.
Instead of writing one set of rules for a card in the main layout and another set for the same card in a sidebar, you can teach the card how to respond to the space it actually has.
What You Will Build
You will build one reusable tutorial card component. The same card will appear in three places:
- a narrow sidebar
- a standard tutorial grid
- a featured wide area
The component will have a narrow stacked layout, a medium state with more comfortable spacing, and a wide horizontal layout when the container has enough room.
Starter HTML
Start with three different layout areas. Each one uses the same .tutorial-card component inside a .card-container wrapper.
<section class="demo-layout">
<aside class="sidebar-card-zone">
<h2>Sidebar</h2>
<div class="card-container">
<article class="tutorial-card">
<img src="/images/css-layout.jpg" alt="CSS layout sketch" />
<div class="tutorial-card__content">
<p class="tutorial-card__level">Intermediate CSS</p>
<h3>Container Queries for Reusable Components</h3>
<p>Learn how components can respond to the space they live in.</p>
<a href="#">Start tutorial</a>
</div>
</article>
</div>
</aside>
<main class="main-card-zone">
<h2>Tutorial Grid</h2>
<div class="tutorial-grid">
<div class="card-container">
<article class="tutorial-card">
<img src="/images/css-layout.jpg" alt="CSS layout sketch" />
<div class="tutorial-card__content">
<p class="tutorial-card__level">Intermediate CSS</p>
<h3>Container Queries for Reusable Components</h3>
<p>Learn how components can respond to the space they live in.</p>
<a href="#">Start tutorial</a>
</div>
</article>
</div>
<div class="card-container">
<article class="tutorial-card">
<div class="tutorial-card__content">
<p class="tutorial-card__level">Intermediate CSS</p>
<h3>Modern CSS Architecture</h3>
<p>Organise your CSS so it stays readable as your site grows.</p>
<a href="#">Start tutorial</a>
</div>
</article>
</div>
</div>
</main>
<section class="featured-card-zone">
<h2>Featured Tutorial</h2>
<div class="card-container card-container--featured">
<article class="tutorial-card">
<img src="/images/css-layout.jpg" alt="CSS layout sketch" />
<div class="tutorial-card__content">
<p class="tutorial-card__level">Intermediate CSS</p>
<h3>Breaking Layouts Into Reusable Components Without Chaos</h3>
<p>
Learn how to turn layout sections into reusable patterns that can move
around your site without breaking.
</p>
<a href="#">Start tutorial</a>
</div>
</article>
</div>
</section>
</section>Base CSS
Add the basic page and layout styling first. This gives the example a readable structure before the component starts responding to its container.
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: system-ui, sans-serif;
line-height: 1.5;
color: #1f1f1f;
background: #f7f4ef;
}
img {
max-width: 100%;
display: block;
}
a {
color: currentColor;
font-weight: 700;
}
.demo-layout {
width: min(100% - 2rem, 72rem);
margin-inline: auto;
padding-block: clamp(2rem, 6vw, 5rem);
display: grid;
gap: 2rem;
}
.sidebar-card-zone,
.main-card-zone,
.featured-card-zone {
padding: 1rem;
border: 1px solid #ddd3c7;
border-radius: 1rem;
background: #fffaf4;
}
.tutorial-grid {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
}Then add the default card styling:
.tutorial-card {
overflow: hidden;
border: 1px solid #d8cec1;
border-radius: 1rem;
background: white;
}
.tutorial-card img {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.tutorial-card__content {
display: grid;
gap: 0.75rem;
padding: 1rem;
}
.tutorial-card__content > * {
margin: 0;
}
.tutorial-card__level {
font-size: 0.8rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
}At this point, the card is reusable, but it does not yet know how much space it has. That is what container queries will add.
Why Media Queries Are Not Enough for Reusable Components
A media query responds to the viewport:
@media (min-width: 50rem) {
.tutorial-card {
display: grid;
grid-template-columns: 1fr 2fr;
}
} This says that when the browser is at least 50rem wide, every tutorial card becomes horizontal. That might work on one page. It starts to wobble when one of those cards is inside a narrow sidebar.
The browser is wide, so the media query applies. The sidebar is narrow, so the card gets squashed. The problem is not that media queries are bad. The problem is that the viewport is not always the thing the component needs to respond to.
CSS Checkpoint: Media query or container query?
Choose the query based on the question the layout is asking.
- What is the main difference between a media query and a container query?
- Why is .card-container a better query container than .tutorial-card in this example?
- When should a page-level media query still be used?
Tips to Remember:
- If the whole page layout needs to change, use a media query.
- If a reusable component needs to adapt to its own available space, use a container query.
- Media queries and container queries can work together.
Show sample answers
- A media query responds to viewport or device conditions. A container query responds to the size or state of a query container around the component.
- Container queries style descendants of the query container. The wrapper becomes the measured context, and the card inside can change safely.
- Use media queries for broad page decisions such as navigation, page grid structure, print styles, motion preferences, colour scheme, and device or viewport conditions.
How confident are you with this concept?
😕 Still confused | 🤔 Getting there | 😊 Got it! | 🎉 Could explain it to a friend!
Step 1: Create a Query Container with container-type
Before you can query a container, tell the browser which element should behave as a query container:
.card-container {
container-type: inline-size;
} This creates a containment context based on the container's inline-size. In most English-language layouts, that means horizontal width.
Use container-type: inline-size most of the time for cards, panels, lists, and navigation components. It means "watch this container's width." The broader size value watches both inline and block dimensions, which is useful less often and can be more restrictive.
Important: the query container is usually not the component itself. In this example, .card-container is the query container. The .tutorial-card inside it changes.
Step 2: Use @container
Once a query container exists, use @container to apply styles when that container reaches a certain size:
@container (min-width: 32rem) {
.tutorial-card {
display: grid;
grid-template-columns: 1fr 2fr;
}
.tutorial-card img {
height: 100%;
aspect-ratio: auto;
}
}The browser is not checking the viewport here. It is checking the nearest ancestor with a containment context. Now the same card can be stacked in a narrow sidebar and horizontal in a wider feature area.
Same component. Different container. Better behaviour.
Step 3: Add Component Variants
Components do not need to jump from tiny to huge. You can create smaller layout adjustments first:
@container (min-width: 22rem) {
.tutorial-card__content {
padding: 1.25rem;
}
.tutorial-card h3 {
font-size: 1.35rem;
}
}Then add the larger layout change:
@container (min-width: 36rem) {
.tutorial-card {
display: grid;
grid-template-columns: minmax(12rem, 0.8fr) 1.2fr;
}
.tutorial-card img {
height: 100%;
aspect-ratio: auto;
}
.tutorial-card__content {
align-content: center;
}
}The narrow state stays as the default. The medium state adds breathing room. The wide state becomes horizontal. This is the real power of container queries: the component becomes responsive to its own available space.
CSS Checkpoint: Component variants
A component variant is a version of the same component that changes based on context.
- Why should the narrow version be the default?
- What makes this different from adding a class for every layout location?
Show sample answers
- A narrow default keeps the component safe in tight spaces, old layouts, sidebars, and small screens. Wider states can enhance from there.
- The same component can respond to available space without needing separate sidebar, grid, and feature classes for the card itself.
How confident are you with this concept?
😕 Still confused | 🤔 Getting there | 😊 Got it! | 🎉 Could explain it to a friend!
Step 4: Named Containers
So far, the @container rule uses the nearest available query container. That is fine for many components. Sometimes, especially in nested layouts, you may want to target a specific container.
.card-container {
container-name: tutorial-card;
container-type: inline-size;
}
/* Shorthand version */
.card-container {
container: tutorial-card / inline-size;
}
@container tutorial-card (min-width: 36rem) {
.tutorial-card {
display: grid;
grid-template-columns: minmax(12rem, 0.8fr) 1.2fr;
}
} Named containers are useful when components are nested, several parent elements have container-type, or you want to be explicit about which container controls the style.
For this lesson, use container: tutorial-card / inline-size. It is compact and clear.
Step 5: Container Query Units
Container query units are length units based on the query container. They are similar in spirit to viewport units like vw and vh, except they relate to the container instead of the browser window.
| Unit | Meaning |
|---|---|
cqw | 1% of the query container's width |
cqh | 1% of the query container's height |
cqi | 1% of the query container's inline size |
cqb | 1% of the query container's block size |
cqmin | The smaller value of cqi or cqb |
cqmax | The larger value of cqi or cqb |
For most component work, start with cqi:
.tutorial-card h3 {
font-size: clamp(1.1rem, 5cqi, 1.6rem);
}
.tutorial-card__content {
padding: clamp(1rem, 4cqi, 1.75rem);
}This makes text and spacing feel more naturally responsive inside different layout areas. Not because the page changed. Because the component's own available space changed.
CSS Checkpoint: Container units
Viewport units respond to the browser window. Container query units respond to the query container.
- Why is cqi often more useful than vw inside a reusable card?
Show sample answer
- cqi scales with the card container, while vw scales with the whole browser window. A sidebar card should not size its heading as though it owned the full viewport.
How confident are you with this concept?
😕 Still confused | 🤔 Getting there | 😊 Got it! | 🎉 Could explain it to a friend!
Step 6: Final Component CSS
Here is the finished version of the component CSS:
.card-container {
container: tutorial-card / inline-size;
}
.tutorial-card {
overflow: hidden;
border: 1px solid #d8cec1;
border-radius: 1rem;
background: white;
}
.tutorial-card img {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.tutorial-card__content {
display: grid;
gap: 0.75rem;
padding: clamp(1rem, 4cqi, 1.75rem);
}
.tutorial-card__content > * {
margin: 0;
}
.tutorial-card__level {
font-size: 0.8rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.tutorial-card h3 {
font-size: clamp(1.1rem, 5cqi, 1.6rem);
line-height: 1.15;
}
.tutorial-card a {
justify-self: start;
}
@container tutorial-card (min-width: 22rem) {
.tutorial-card__content {
gap: 1rem;
}
}
@container tutorial-card (min-width: 36rem) {
.tutorial-card {
display: grid;
grid-template-columns: minmax(12rem, 0.8fr) 1.2fr;
}
.tutorial-card img {
height: 100%;
aspect-ratio: auto;
}
.tutorial-card__content {
align-content: center;
}
.tutorial-card:not(:has(img)) {
display: block;
}
}Media Queries vs Container Queries
Media queries are not wrong. They are still useful. The trick is knowing which question you are asking.
| Use media queries when... | Use container queries when... |
|---|---|
| The whole page layout needs to change | A component needs to adapt to its own available space |
| You are responding to viewport width | You are responding to parent or container width |
| You are changing page-level navigation | You are changing a card, panel, widget, or component layout |
| You need print, motion, colour scheme, or device preferences | You need component-level responsive behaviour |
| You are setting broad layout rules | You are making reusable components more flexible |
Example media query:
@media (min-width: 60rem) {
.demo-layout {
grid-template-columns: 16rem 1fr;
}
.featured-card-zone {
grid-column: 1 / -1;
}
}Example container query:
@container tutorial-card (min-width: 36rem) {
.tutorial-card {
grid-template-columns: minmax(12rem, 0.8fr) 1.2fr;
}
}Use both. Just do not ask media queries to solve every component problem. That is how stylesheets become a cupboard full of mystery cables.
Common Mistakes and Defensive Testing
Reusable components often receive imperfect content. A component that only works with perfect demo copy is not reusable yet.
Common mistakes
- Forgetting
container-type: without an eligible query container, the browser has nothing useful to query. - Putting the container on the wrong element: container queries style descendants, so a wrapper is usually clearer.
- Using container queries for everything: page-level layout decisions still belong with media queries.
- Forgetting narrow states: start with the safe stacked layout, then enhance it.
- Not testing missing content: cards without images or descriptions should still look intentional.
Defensive test cases
Add these cases to your HTML:
<h3>
Understanding Container Queries When Your Component Lives Somewhere Deeply
Inconvenient
</h3>
<article class="tutorial-card">
<div class="tutorial-card__content">
<p class="tutorial-card__level">Intermediate CSS</p>
<h3>Modern CSS Architecture</h3>
<p>Organise your CSS so your future self does not quietly resent you.</p>
<a href="#">Start tutorial</a>
</div>
</article>
<article class="tutorial-card">
<img src="/images/css-layout.jpg" alt="CSS layout sketch" />
<div class="tutorial-card__content">
<p class="tutorial-card__level">Intermediate CSS</p>
<h3>Subgrid and Layout Alignment</h3>
<a href="#">Start tutorial</a>
</div>
</article>Then test the same component in different container widths:
.test-narrow {
width: 14rem;
}
.test-medium {
width: 24rem;
}
.test-wide {
width: 44rem;
}If a no-image card looks odd in the wide layout, add a defensive rule:
@container tutorial-card (min-width: 36rem) {
.tutorial-card:not(:has(img)) {
display: block;
}
}CSS Checkpoint: Defensive component thinking
A reusable component should not rely on perfect content.
- What should you test before reusing a card across a site?
- Why is finding a broken stress case useful?
Show sample answers
- Test long content, missing content, narrow containers, wide containers, and browser zoom.
- It shows where the component needs a stronger default, a defensive rule, or a simpler variant before real users encounter the problem.
How confident are you with this concept?
😕 Still confused | 🤔 Getting there | 😊 Got it! | 🎉 Could explain it to a friend!
Guided Practice
Build the reusable tutorial card
Create the query container, add component variants, then move the same card through different layout contexts.
Step 1: Wrap one card
Add a .card-container wrapper around one .tutorial-card. Keep the card HTML itself reusable.
💡 Need a hint?
Step 2: Create the query container
Add container: tutorial-card / inline-size; to .card-container.
💡 Need a hint?
Step 3: Add a medium state
Use @container tutorial-card (min-width: 22rem) to increase the card content gap or padding.
💡 Need a hint?
Step 4: Add a wide state
Use @container tutorial-card (min-width: 36rem) to make image-based cards horizontal.
💡 Need a hint?
Step 5: Move the same card
Place the same card in a narrow sidebar, a grid column, and a wide feature area. The HTML should stay the same while the available space changes.
💡 Need a hint?
You are on track if:
- ☐ The wrapper has a named query container
- ☐ The card has a narrow default layout
- ☐ The medium state improves spacing without changing the whole component
- ☐ The wide state creates a horizontal card only when the container has room
- ☐ The same component works in at least three layout contexts
Independent Practice
CSS Independent Practice: Create a reusable resource card
Now apply the same responsive component thinking to a new card.
Your Task:
Create a .resource-card component with an optional image, category label, title, short description, and link. Place the same card in a narrow sidebar, a standard grid column, and a wide featured area.
After testing, write a short reflection answering: did the component respond to the viewport, or to the space it actually had?
Requirements:
- Use container-type or the container shorthand
- Use at least two @container rules
- Use one named container
- Use one container query unit such as cqi
- Include one narrow default layout and one wide layout variant
- Test a long title, missing image, missing description, narrow width, and wide width
Stretch Goals (Optional):
- Add a defensive :has() rule for optional images
- Compare one media query and one container query in a short note
Success Criteria:
| Criteria | You've succeeded if... |
|---|---|
| Container setup | The resource card has a wrapper that uses container-type or the container shorthand, including one named container. |
| Component variants | The component has a narrow default state and at least two @container rules for wider available space. |
| Container units | At least one size or spacing value uses a container query unit such as cqi. |
| Defensive testing | The card is tested with a long title, missing image, missing description, narrow container, and wide container. |
Lesson Complete: You Can Build Context-Aware Components
Key Takeaways:
- Media queries respond to the viewport or device; container queries respond to component context.
- A size container query needs a containment context, usually with container-type: inline-size.
- Named containers make larger component systems easier to reason about.
- Container query units such as cqi let type and spacing scale with component space.
- Reusable components need narrow defaults and defensive tests for imperfect content.
Learning Objectives Review:
Look back at what you set out to learn. Can you now:
- ✅ Explain why media queries are not always enough for reusable components Check!
- ✅ Create a query container with container-type or the container shorthand Got it!
- ✅ Use @container to change component styles based on container width Can explain it!
- ✅ Use named containers to target a specific layout context Could teach this!
- ✅ Use container query units such as cqi, cqw, cqmin, and cqmax Check!
- ✅ Stress-test a reusable component with long, missing, narrow, and wide content cases Got it!
If you can confidently answer "yes" to most of these, you're ready to move on!
Think & Reflect:
Reusable Components
- Where have you previously used a media query to solve a component-level problem?
- How would a container query have made that component easier to reuse?
Stress Testing
- Which stress test revealed the weakest part of your card layout?
- What narrow default should your component always be able to fall back to?
🤔 Real-World Test:
Container queries change the mental model from how wide is the browser? to how much space does this component have? That one distinction makes reusable CSS calmer and easier to maintain.
🎯 Looking Ahead:
Next, move into Modern CSS Architecture. You will connect layout primitives, component styles, custom properties, cascade layers, and reusable patterns so your CSS stays understandable as the project grows.
Recommended Next Steps
Continue Learning
Ready to move forward? Continue with the next tutorial in this series:
Test and Validate Your SiteRelated Topics
Explore these related tutorials to expand your knowledge:
Additional Resources
Deepen your understanding with these helpful resources:
- MDN: CSS container queries - MDN guide to container queries, container-type, named containers, and container query units.
- MDN: @container - Reference for the @container at-rule and supported query conditions.
- MDN: container - Reference for the container shorthand that combines container-name and container-type.
- web.dev: Learn CSS container queries - A practical explanation of how container queries fit into responsive component design.