Responsive Design in 2026: Container Queries and Subgrid
On this page
Responsive Design in 2026: Container Queries and Subgrid
For the better part of two decades, responsive design meant one thing: media queries. We sprinkled @media (min-width: 768px) breakpoints across our stylesheets and hoped the viewport was a good proxy for what any given component actually needed. In 2026, that assumption is finally, mercifully behind us. Container queries and subgrid are now baseline-available across every evergreen browser, and they change the mental model of responsive design from "how big is the screen?" to "how much room does this component have?"
This post is a practical tour of both features, how they work together, and the patterns that hold up in production.
Why Media Queries Were Never Enough
The core flaw of viewport-based media queries is that a component doesn't live in the viewport — it lives in a container. A card in a full-width hero has completely different space than the same card in a narrow sidebar, yet a media query treats them identically because the viewport is the same width in both cases.
The workarounds were ugly: utility classes toggled by JavaScript, duplicated component variants (Card, CardCompact, CardSidebar), or brittle breakpoints tuned to one specific layout. Every one of these coupled a component's styling to knowledge of where it would be used — the opposite of reusable design.
Container queries sever that coupling. A component asks about its own available space and styles itself accordingly, no matter where you drop it.
Container Queries: The Fundamentals
To query a container, you first have to declare one. You do this with container-type on the parent element:
.card-grid {
container-type: inline-size;
container-name: cards;
}
inline-size means "let me query the inline dimension" — width in horizontal writing modes. This is the option you want 95% of the time. size queries both dimensions but requires the container to have an explicit block size, which is a common footgun. Naming the container is optional but strongly recommended once you have nested containers.
Now the child can respond to its container's width:
.card {
display: grid;
gap: 1rem;
}
@container cards (min-width: 400px) {
.card {
grid-template-columns: 150px 1fr;
}
}
Drop that .card into a 300px sidebar and it stacks. Drop it into a 900px main column and it goes side-by-side. Same markup, same class, zero JavaScript, zero variant classes. The component is genuinely portable.
Container Query Units
Alongside the queries themselves, you get container-relative length units: cqw, cqh, cqi (inline), cqb (block), cqmin, and cqmax. These are the container-scoped answer to viewport units like vw.
.card__title {
font-size: clamp(1rem, 5cqi, 2rem);
}
That title now scales relative to the card's inline size rather than the browser window — fluid typography that actually respects the component's context.
Subgrid: Aligning Across Boundaries
Container queries solve "how do I respond to available space." Subgrid solves a different, equally old problem: aligning content across sibling elements that each have their own internal structure.
Picture a row of cards, each with a title of varying length, a body, and a footer button. Before subgrid, getting all the footers to line up meant fixed heights, flexbox hacks, or JavaScript measuring. Subgrid lets each card inherit its parent grid's tracks:
.card-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1.5rem;
}
.card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
}
Now every card participates in the same row tracks. All titles occupy row 1, all bodies row 2, all footers row 3 — perfectly aligned across the entire list, regardless of content length. No magic numbers.
The subgrid keyword works on grid-template-columns too, which is invaluable for form layouts where you want labels and inputs to align across multiple fieldsets.
Putting Them Together
The real power shows up when you combine the two. Consider a dashboard where a widget both needs to reflow based on its own width and keep its internal rows aligned with siblings.
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 1rem;
}
.widget {
container-type: inline-size;
display: grid;
grid-template-rows: subgrid;
grid-row: span 2;
}
@container (min-width: 500px) {
.widget__header {
display: flex;
justify-content: space-between;
}
}
The widget aligns with its neighbors via subgrid and rearranges its own header based on the space it was given. This is component-driven responsive design in its mature form: layout decisions made locally, by the components that own them.
Practical Migration Advice
You don't need to rip out every media query. Here's a pragmatic path:
- Keep media queries for global layout. Page-level scaffolding — sidebar-versus-no-sidebar, overall page margins, print styles — is genuinely viewport-dependent. Media queries remain the right tool there.
- Convert components to container queries. Any reusable component (cards, media objects, nav items, form groups) should query its container, not the viewport. This is where you get the biggest maintainability win.
- Add a
@supportsguard only if you must. Baseline support is excellent in 2026, but if your analytics still show meaningful traffic from frozen enterprise browsers, wrap enhancements in@supports (grid-template-rows: subgrid)and ship a flexbox fallback. - Watch your container context. A
container-typeofinline-sizeestablishes containment, which can collapse the block size of the element. If something disappears, that's usually why — query a wrapper rather than the sizing element itself. - Don't over-name. Named containers are great for nested cases, but anonymous
@containerqueries resolve to the nearest ancestor container and are often all you need.
Performance Notes
A common worry is that container queries are expensive. In practice, browsers in 2026 optimize containment aggressively — declaring a container actually gives the engine more information to isolate layout work, which can improve performance on complex pages. The one thing to avoid is applying container-type: size broadly, since two-dimensional containment forces the browser to resolve block size eagerly. Stick to inline-size unless you have a concrete need.
Subgrid has effectively zero runtime cost beyond normal grid layout; it's resolved at layout time like any other grid.
Accessibility and Content Reflow
Neither feature changes DOM order, which is exactly what you want — visual reordering that leaves the accessibility tree intact. But remember that container queries make it easier to create layouts that reflow dramatically. Always test with 400% zoom and ensure your reflows still meet WCAG 1.4.10, and verify that focus order still makes sense in each configuration. Because the source order is untouched, this is usually straightforward, but it's worth an explicit check.
FAQ
Do container queries replace media queries entirely?
No. Use container queries for components and media queries for page-level layout, plus things a container can't know about — prefers-reduced-motion, prefers-color-scheme, print, and viewport-level page structure.
What's the difference between container-type: size and inline-size?
inline-size lets you query width only and is the safe default. size queries both dimensions but requires the container to have a definite block size, which often breaks intrinsic height. Reach for size only when you genuinely need to query height.
Can I nest containers?
Yes. A @container query resolves against the nearest ancestor that is a container. Use container-name to target a specific ancestor when nesting gets deep, e.g. @container cards (min-width: 400px).
Does subgrid work in both directions?
Yes. You can set grid-template-rows: subgrid, grid-template-columns: subgrid, or both. Each axis independently inherits the parent grid's tracks; the other axis can define its own.
What about older browsers?
As of 2026 both features are Baseline and supported in all current evergreen browsers. For legacy enterprise environments, gate enhancements behind @supports and provide a flexbox or single-column fallback — the graceful-degradation story is clean.
Do container query units replace viewport units?
They complement them. Use cqi/cqb for typography and spacing that should scale with a component, and keep vw/vh/dvh for genuinely viewport-relative things like full-screen sections.
Conclusion
The shift from viewport-thinking to container-thinking is the most significant change in responsive CSS since flexbox. Container queries let components own their responsive behavior, and subgrid lets them align cleanly across boundaries. Together they eliminate a whole category of hacks — variant classes, JavaScript measuring, magic-number heights — and let you build components that are genuinely portable. If you're still writing viewport breakpoints for individual components in 2026, it's time to let them go.