Why Container Queries Change Everything About Responsive Design
For years, responsive design meant writing breakpoints based on the browser's viewport width. That worked well enough when layouts were simple — but modern component-driven UIs have outgrown it. A card component placed in a narrow sidebar behaves completely differently from the same card in a full-width hero section, yet viewport-based media queries treat them identically.
CSS Container Queries solve this. They let components respond to the size of their own parent container rather than the screen. As of 2026, browser support is universal and the Container Query spec has stabilised — making now the ideal time to build your responsive design system around this approach.
This tutorial walks you through building a practical, scalable responsive design system using Container Queries from the ground up. Whether you're a designer who codes, a frontend developer, or a full-stack team at an SMB in Australia, Singapore, Canada, or the US — this guide gives you a working system you can ship.
What You'll Need
- Basic familiarity with CSS and HTML
- A code editor (VS Code recommended)
- Node.js 20+ if you're working inside a framework like Next.js, Astro, or Vite
- A modern browser (Chrome 120+, Safari 17+, Firefox 120+ — all support Container Queries fully)
- Optional: Figma for designing components before building them
Step 1: Understand the Container Query Mental Model
Before writing a single line of CSS, shift how you think about layout. Instead of asking "How wide is the screen?", ask "How wide is the box this component lives in?"
Container Queries introduce two key concepts:
- Containment context: A container you explicitly declare as the reference point.
- Container query rule: A conditional style block that applies when the container meets a size condition.
Here's the simplest possible example to anchor the concept:
/* Declare a containment context */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* Style the card based on its container's width */
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
Pro tip: Use container-type: inline-size in most cases. It tracks the horizontal axis (width) without requiring containment on the block axis, which avoids layout side effects.
Step 2: Define Your Design Tokens First
A solid responsive system starts with design tokens — the shared vocabulary of spacing, typography, and sizing that every component references. In 2026, the standard approach pairs CSS custom properties with @layer to keep specificity predictable.
Create a tokens.css file:
@layer tokens {
:root {
/* Spacing scale */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-12: 3rem;
/* Type scale */
--text-sm: clamp(0.8rem, 1.5vw, 0.875rem);
--text-base: clamp(1rem, 2vw, 1.125rem);
--text-lg: clamp(1.125rem, 2.5vw, 1.5rem);
--text-xl: clamp(1.5rem, 4vw, 2.25rem);
/* Radius */
--radius-sm: 0.375rem;
--radius-md: 0.75rem;
--radius-lg: 1.25rem;
/* Breakpoints (for wrapper-level media queries) */
--bp-md: 48em;
--bp-lg: 64em;
}
}
Notice that typography uses clamp() — this gives you fluid type scaling at the document level while Container Queries handle component-level layout. The two work together, not against each other.
Step 3: Create a Container Wrapper Utility
Rather than scattering container-type declarations across your codebase, centralise them in a reusable utility class. This keeps your system consistent and auditable.
@layer utilities {
.cq-root {
container-type: inline-size;
}
.cq-root-named {
container-type: inline-size;
container-name: component;
}
}
Apply .cq-root to any wrapper element whose children need container-aware styling. In a React or Astro project, this might look like:
<div class="cq-root">
<ArticleCard title="..." image="..." />
</div>
Common pitfall: Don't apply container-type directly to the component's root element itself. The container must be an ancestor — the element being queried cannot query itself.
Step 4: Build Your First Container-Aware Component
Let's build a real-world component: a product card used across an e-commerce layout. It needs to display in three configurations depending on context — compact (in a sidebar), standard (in a three-column grid), and expanded (in a featured slot).
HTML Structure
<div class="cq-root">
<div class="product-card">
<div class="product-card__image">
<img src="product.jpg" alt="Product name" />
</div>
<div class="product-card__body">
<span class="product-card__badge">New</span>
<h3 class="product-card__title">Product Name</h3>
<p class="product-card__description">A short description of the product.</p>
<div class="product-card__footer">
<span class="product-card__price">$49.00</span>
<button class="btn">Add to cart</button>
</div>
</div>
</div>
</div>
Container-Aware CSS
@layer components {
/* Base (compact): stacked, image on top */
.product-card {
display: flex;
flex-direction: column;
gap: var(--space-3);
border-radius: var(--radius-md);
overflow: hidden;
}
.product-card__description {
display: none;
}
.product-card__badge {
display: none;
}
/* Standard: side-by-side layout at 280px+ */
@container (min-width: 280px) {
.product-card__badge {
display: inline-block;
font-size: var(--text-sm);
padding: var(--space-1) var(--space-2);
}
}
/* Expanded: full layout with description at 420px+ */
@container (min-width: 420px) {
.product-card {
flex-direction: row;
align-items: flex-start;
gap: var(--space-6);
}
.product-card__image {
flex: 0 0 40%;
}
.product-card__description {
display: block;
font-size: var(--text-sm);
color: #666;
}
.product-card__footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: var(--space-4);
}
}
}
Now this single card component adapts intelligently wherever it's placed — no custom props, no JavaScript resize observers, no duplicated component variants.
Step 5: Handle Container Query Units
One underused feature of the Container Query spec is container query length units. Similar to viewport units (vw, vh), you can now use:
cqi— 1% of the container's inline size (width)cqb— 1% of the container's block size (height)cqmin/cqmax— the smaller or larger of the two
Use these for typography or spacing that should scale relative to the component's container, not the viewport:
.product-card__title {
font-size: clamp(1rem, 4cqi, 1.5rem);
}
Pro tip: Combine cqi units with clamp() to set hard lower and upper bounds. This prevents type from becoming illegibly small in compact contexts or absurdly large in wide ones.
Step 6: Integrate with Your Layout System
Container Queries don't replace viewport media queries — they complement them. Your page-level layout (column counts, sidebar widths, overall spacing) still belongs in media queries. Container Queries handle component-level adaptation.
A practical rule of thumb used by the team at Lenka Studio: media queries for layout, container queries for components.
/* Page layout — media query */
@media (min-width: 64em) {
.page-grid {
display: grid;
grid-template-columns: 280px 1fr;
gap: var(--space-8);
}
}
/* Component inside sidebar adapts automatically via container queries */
.sidebar .cq-root {
/* No extra CSS needed — the card reads its own container width */
}
Step 7: Test Across Real Layouts
Container Queries are only as reliable as your test coverage. Unlike viewport testing, you need to verify components inside different parent widths simultaneously on the same page.
Here's a simple test harness you can drop into any dev environment:
<div style="display: grid; grid-template-columns: 200px 380px 600px; gap: 2rem; padding: 2rem;">
<div class="cq-root"><ProductCard /></div>
<div class="cq-root"><ProductCard /></div>
<div class="cq-root"><ProductCard /></div>
</div>
This renders the same component at three different container widths side by side — instantly revealing any layout issues. For automated testing, pair this with Playwright's visual comparison or Chromatic if you're using Storybook.
Common pitfall: Don't forget to test container query behaviour during resize animations or transitions (e.g. collapsing sidebars). If the parent container's width transitions, the container query triggers mid-animation — which can cause jarring layout shifts if you haven't accounted for it.
Step 8: Document Your System for Handoff
A design system is only valuable if the whole team can use it consistently. Document your container breakpoints the same way you'd document colour tokens or spacing scales.
In Figma, use annotations or the Variables panel to record the container width thresholds alongside each component. Pair this with a short README in your codebase:
# Container Query Breakpoints
| Breakpoint | Width | Layout Behaviour |
|------------|-----------|----------------------------|
| Compact | < 280px | Stacked, minimal info |
| Standard | 280–419px | Badge visible, stacked |
| Expanded | 420px+ | Side-by-side, full detail |
If you're running a content or marketing team alongside your dev work, keeping your design documentation and marketing workflows aligned matters just as much — tools like the Lenka Studio Social Media Toolkit help you maintain that parallel rhythm without losing momentum.
Common Pitfalls to Avoid
- Querying without a containment context: If you forget to declare
container-typeon an ancestor, your@containerrules silently do nothing. - Over-nesting containers: Every containment boundary has a small layout cost. Only declare containers where components genuinely need to respond independently.
- Mixing container and viewport queries inconsistently: Choose a clear rule and stick to it — confusion between the two is the most common source of bugs in new systems.
- Skipping the design phase: Build your container breakpoints in Figma or on paper before writing CSS. Jumping straight to code often leads to arbitrary thresholds that don't map to real content needs.
Next Steps
You now have a working foundation for a container-query-driven design system — from tokens and utility classes through to documented, testable components. The next natural steps are:
- Expand the system to cover navigation components, modals, and data tables — all of which benefit significantly from container-aware styling
- Explore
style()container queries (part of the newer CSS spec) for querying CSS custom property values, not just dimensions - Integrate your token layer with a design tool pipeline using Figma Variables and Style Dictionary for true single-source-of-truth design tokens
- Run a quick check on how your overall brand experience is holding up across touchpoints — the Lenka Studio Brand Health Score is a free assessment that takes about five minutes and surfaces gaps you might not have noticed
If you're building a design system for a product or scaling a frontend codebase and want expert support, the team at Lenka Studio works with SMBs across Australia, Singapore, Canada, and the US to design and build UI systems that hold up at scale. Get in touch — we'd love to hear what you're working on.




