Responsive Components with Container Queries: A 2026 Pattern Library for Real Layout Problems
Media queries have a fundamental limitation that most developers learned to work around rather than fix: they respond to the viewport, not to the space a component actually occupies. A card component in a full-width layout might need to switch from horizontal to vertical at 600 pixels. That same card dropped into a sidebar needs the switch at 300 pixels because the sidebar itself is only 350 pixels wide. With media queries, you end up writing separate classes for sidebar cards and main-content cards—duplicating layout logic that should be identical except for the breakpoint.
Container queries fix this. A component styled with @container responds to the width of its parent container, not the viewport. The card in the sidebar adapts when the sidebar is narrow. The same card in a full-width area adapts when the viewport is narrow. One set of styles, no duplication, no context-specific classes. In 2026, container queries have full support across Chrome, Edge, Firefox, and Safari, and they have moved from experimental curiosity to essential layout tool.
This guide is a pattern library. Rather than explaining the specification abstractly, it covers the specific layout problems container queries solve and provides ready-to-use patterns for each.
The Syntax in Thirty Seconds
Two declarations make container queries work:
/* Step 1: Declare a containment context on the parent */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* Step 2: Write conditional styles for the child */
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
gap: 1.5rem;
}
}
container-type: inline-size tells the browser to track the inline (horizontal) size of the element. container-name gives it an identifier you can reference in @container rules. When the named container is at least 400 pixels wide, the card switches from its default stacked layout to a side-by-side grid.
You can omit container-name and query the nearest ancestor with containment:
@container (min-width: 400px) {
.card { /* styles */ }
}
Named containers are clearer when you have nested containment contexts. For simple layouts, anonymous queries work fine.
Pattern 1: The Responsive Card
The card component is the canonical container query example because it illustrates the problem perfectly. Cards appear in grids, sidebars, modals, and full-width sections. Each context provides a different amount of horizontal space. The card needs to look right in all of them.
Default State: Stacked
.card-container {
container-type: inline-size;
}
.card {
display: flex;
flex-direction: column;
}
.card img {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.card-body {
padding: 1rem;
}
Horizontal at 400px
@container (min-width: 400px) {
.card {
flex-direction: row;
}
.card img {
width: 40%;
aspect-ratio: 1;
}
.card-body {
padding: 1.5rem;
}
}
Featured at 700px
@container (min-width: 700px) {
.card {
flex-direction: row;
}
.card img {
width: 50%;
aspect-ratio: 16 / 9;
}
.card-body {
padding: 2rem;
}
.card h3 {
font-size: 1.5rem;
}
}
Drop this card anywhere—a three-column grid, a sidebar, a single-column layout—and it adapts to the space available. No .card--sidebar or .card--featured modifier classes. The component is self-contained.
Pattern 2: Sidebar Navigation That Adapts
Navigation in a sidebar presents a specific problem: when the sidebar is wide enough, you might want icons alongside text labels. When the sidebar collapses (on narrow viewports or when a user hides it), the navigation should switch to icon-only mode. Media queries cannot handle this because the sidebar width is independent of the viewport—it depends on the layout configuration.
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
.nav-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem 1rem;
}
.nav-label {
display: none;
}
@container sidebar (min-width: 180px) {
.nav-label {
display: inline;
}
}
@container sidebar (min-width: 280px) {
.nav-item {
padding: 0.75rem 1.25rem;
font-size: 1rem;
}
}
At narrow widths, only icons show. At 180 pixels, labels appear. At 280 pixels, padding increases for a more spacious feel. The navigation component does not know or care about the viewport—it only knows how much space the sidebar gives it.
This pattern works well in DFM2HTML templates where the sidebar width might vary between the Template 1 and Template 2 layouts. The navigation styles stay the same regardless.
Pattern 3: Flexible Hero Sections
Hero sections are notoriously difficult to make responsive because they combine large images, headings, subtext, and call-to-action buttons in a layout that varies dramatically between mobile and desktop. Container queries make this manageable when the hero lives inside a layout region that is not full-width—inside a content area with a sidebar, for example.
.hero-wrapper {
container-type: inline-size;
container-name: hero;
}
.hero {
display: flex;
flex-direction: column;
text-align: center;
padding: 2rem 1rem;
}
.hero h1 {
font-size: clamp(1.5rem, 4cqw, 3rem);
line-height: 1.2;
}
.hero .cta-group {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
@container hero (min-width: 600px) {
.hero {
flex-direction: row;
text-align: left;
align-items: center;
gap: 2rem;
padding: 3rem 2rem;
}
.hero-content {
flex: 1;
}
.hero-image {
flex: 1;
}
.hero .cta-group {
flex-direction: row;
}
}
Notice 4cqw in the clamp() for the heading. That is a container query unit—4% of the container’s inline width. Container query units (cqw, cqh, cqi, cqb) let you size elements relative to the container rather than the viewport. For typography that should scale with its container, these units are far more predictable than viewport units inside components.
Container Query Units: cqw, cqh, and Friends
Container query units deserve specific attention because they solve a problem that viewport units (vw, vh) created: text and spacing scaled to the viewport look wrong inside constrained containers.
| Unit | Meaning |
|---|---|
cqw | 1% of the container’s inline size (width in horizontal writing) |
cqh | 1% of the container’s block size (height) |
cqi | 1% of the container’s inline size (logical) |
cqb | 1% of the container’s block size (logical) |
cqmin | The smaller of cqi and cqb |
cqmax | The larger of cqi and cqb |
In practice, cqw and cqi are the ones you will use most. They are interchangeable for horizontal writing modes. Use them for:
- Fluid typography within components.
font-size: clamp(1rem, 3cqw, 2rem)scales the heading with the container width, clamped to sensible minimums and maximums. - Responsive padding.
padding: 2cqwgives proportional spacing that grows with the container. - Icon sizing.
width: 5cqwmakes icons scale with available space rather than being fixed or viewport-relative.
Container query units require a containment context to resolve. If no ancestor has container-type set, the units fall back to the viewport—identical to vw and vh. Always ensure a containment context exists.
Container Queries vs Media Queries: When to Use Which
Container queries do not replace media queries. They serve different purposes, and using the right tool for the job matters.
Use media queries for:
- Page-level layout changes. Switching from a single-column to a two-column layout based on viewport width is a page concern, not a component concern.
- Orientation-based adjustments.
@media (orientation: portrait)affects the whole page. - User preference queries.
prefers-reduced-motion,prefers-color-scheme, andprefers-contrastare viewport-level settings. - Print styles.
@media printcontrols the print layout for the entire document.
Use container queries for:
- Component-internal layout. How a card, navigation block, or hero section arranges its children based on available space.
- Reusable patterns that appear in different layout contexts. Any component that might be placed in a full-width area, a sidebar, a modal, or a grid cell.
- Typography and spacing that should scale with the containing element rather than the viewport.
The ideal stylesheet uses both: media queries for the page grid, container queries for the components within it.
Performance Considerations
Container queries introduce a containment requirement: the browser must be able to determine the container’s size without looking at its children. This is what container-type: inline-size enforces—the children cannot influence the container’s inline dimension.
In practice, this has no performance cost on static sites. The browser already computes element sizes during layout. Containment contexts simply formalise a constraint that was usually true anyway. There is no additional rendering pass, no JavaScript overhead, and no layout thrashing.
If your Core Web Vitals are clean, adding container queries will not change that. The CSS parser handles @container rules as efficiently as @media rules. The file size increase from container query CSS is negligible compared to the JavaScript that would otherwise be needed for container-aware components.
On a performance-optimised stylesheet, container queries add capability without adding weight.
Browser Support in 2026
Container queries (@container with size queries) have been supported in all major browsers since 2023:
- Chrome / Edge: Supported since version 105 (September 2022)
- Firefox: Supported since version 110 (February 2023)
- Safari: Supported since version 16 (September 2022)
Container query units (cqw, cqh, etc.) have the same support timeline. Container style queries (@container style(--variable: value)) have more limited support—Chrome and Edge support them, but Firefox and Safari support arrived later. For size-based queries, which cover the vast majority of use cases, browser support is universal.
No polyfill needed. No progressive enhancement required for modern browsers. You can use container queries in production today with full confidence.
How Container Queries Work in DFM2HTML Templates
DFM2HTML’s template system generates clean HTML structures where components sit inside well-defined layout regions—content areas, sidebars, grid cells. This structure is ideal for container queries because the containment boundaries are natural. A card inside a .content-area div responds to the content area’s width. The same card inside a .sidebar div responds to the sidebar’s width.
To add container queries to a DFM2HTML template, you add container-type: inline-size to the parent elements in your stylesheet. The features page describes the CSS structure the editor generates—clean selectors, predictable nesting, no inline styles—which makes adding containment contexts straightforward.
Starting from Template 1 or Template 2, the process is:
- Identify the layout regions where components should adapt (content area, sidebar, grid cells).
- Add
container-type: inline-sizeto those regions in the stylesheet. - Write
@containerrules for the components within them. - Test at different viewport widths to verify that components adapt to their container, not just the viewport.
The result is a template with genuinely responsive components—not just a responsive page grid with fixed-layout components inside it.
Pattern 4: A Responsive Form Layout
Forms are another strong use case. A contact form in a full-width layout might display labels beside inputs. The same form in a narrow sidebar should stack labels above inputs. Container queries handle this cleanly:
.form-wrapper {
container-type: inline-size;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.25rem;
margin-bottom: 1rem;
}
.form-group label {
font-weight: 600;
}
@container (min-width: 500px) {
.form-group {
flex-direction: row;
align-items: baseline;
gap: 1rem;
}
.form-group label {
width: 150px;
flex-shrink: 0;
text-align: right;
}
.form-group input,
.form-group textarea {
flex: 1;
}
}
Below 500 pixels of container width, labels stack above inputs. Above 500 pixels, labels sit to the left. The form adapts regardless of where it appears in the layout.
Pattern 5: Grid Items That Change Their Internal Layout
When you have a CSS grid of items—services, features, portfolio pieces—each grid cell varies in width depending on the column count, which changes with the viewport. Container queries let the items inside those cells adapt to the cell width rather than the viewport:
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
}
.service-item {
container-type: inline-size;
}
.service-content {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 1.5rem;
}
@container (min-width: 350px) {
.service-content {
flex-direction: row;
text-align: left;
align-items: flex-start;
gap: 1.5rem;
}
.service-icon {
flex-shrink: 0;
}
}
On a wide viewport, the grid shows three columns and each service item is wide enough to trigger the horizontal layout. On a narrow viewport, the grid collapses to one column and the items might still be wide enough for horizontal layout—or they might stack vertically. The component does not care about the grid configuration. It only cares about its own width.
Common Mistakes
Forgetting container-type. Without it, @container rules are ignored silently. If your container queries are not working, check that the parent has container-type: inline-size set.
Using container-type: size when you only need inline-size. The size value establishes containment in both dimensions, which requires the element to have an explicit height. For most layout purposes, inline-size (width containment only) is what you want.
Nesting containment contexts accidentally. If a container query target is itself a containment context, @container rules inside it query the nearest ancestor with containment—which might be the element itself, causing unexpected behaviour. Use named containers to be explicit about which ancestor you are querying.
Over-using container queries where media queries are simpler. If a component always occupies the full viewport width, a media query is equivalent and more familiar. Container queries add value when the component appears in varying-width contexts.
Putting It Together
Container queries complete the responsive design story that media queries started. Media queries handle the page layout. Container queries handle the component layout. Together, they give you a stylesheet where every element adapts intelligently to its actual context—not just the viewport dimensions.
For static sites built with DFM2HTML, the practical workflow is clear: use media queries in your main stylesheet for the overall page grid, add container-type to the layout regions that hold reusable components, and write @container rules for those components. The result is templates that look right everywhere without CSS duplication or JavaScript intervention. The tutorials index has more on the CSS and layout techniques that pair well with these patterns.