Responsive Grid Layouts Without Media Queries
The Problem with Breakpoints
Media queries couple your layout to the viewport. Change the viewport breakpoints and every component that relies on them breaks. Wrap a sidebar component in a narrower container and suddenly its internal columns collapse at the wrong width.
Responsive grid layouts without media queries solve this by letting the container width — not the viewport — determine when columns reflow. The grid adapts on its own.
The Core Pattern: auto-fill + minmax
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}
This one declaration does the work of four breakpoints:
| Container Width | Columns | How It Works |
|---|---|---|
| 1200px | 4 columns | 280px × 4 = 1120px — fits |
| 900px | 3 columns | 280px × 3 = 840px — fits |
| 600px | 2 columns | 280px × 2 = 560px — fits |
| 300px | 1 column | 280px × 1 = 280px — fits |
The browser calculates: "How many 280px columns fit in this width? Fill the row, then distribute leftover space as 1fr."
No @media rules. No breakpoint constants. The layout responds to its container.
auto-fill vs auto-fit
The difference matters when you have fewer items than columns:
/* auto-fill: creates empty tracks to fill the row */
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
/* auto-fit: collapses empty tracks so items stretch */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
| Scenario | auto-fill | auto-fit |
|---|---|---|
| 3 items, space for 5 columns | 3 stretched items + 2 empty tracks | 3 items stretch to fill all space |
| 6 items, space for 5 columns | Same — all items fill naturally | Same — all items fill naturally |
Rule of thumb: Use auto-fit when items should always expand. Use auto-fill when you want items to maintain a consistent maximum width.
Constraining Maximum Width
minmax() sets a floor. To also set a ceiling, use min():
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
gap: 24px;
}
The min(280px, 100%) ensures the column never exceeds the container — preventing overflow on very narrow screens where even 280px is too wide.
Variable-Width Sidebars
Combine a fixed sidebar with a fluid main area:
.layout {
display: grid;
grid-template-columns: minmax(200px, 260px) 1fr;
gap: 24px;
}
The sidebar stays between 200px and 260px. The main area takes whatever remains. On very narrow screens, you can let the sidebar wrap below using auto-fill:
.layout {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 24px;
}
This single rule works for both wide (side-by-side) and narrow (stacked) layouts.
Container Queries: Component-Level Responsiveness
Container queries let components respond to their own container width — not the viewport:
.card-container {
container-type: inline-size;
}
@container (min-width: 500px) {
.card {
grid-template-columns: 1fr 1fr;
}
}
Now a .card reformats itself based on the .card-container width. Put the same card in a sidebar (300px) and it stacks vertically. Put it in the main content area (700px) and it goes side-by-side. Same component, different contexts.
| Approach | Responds To | Granularity |
|---|---|---|
| Media queries | Viewport width | Page-wide |
| auto-fill + minmax | Available space | Row-level |
| Container queries | Container width | Component-level |
Real-World Pattern: Responsive Product Grid
.products {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
gap: 20px;
}
.product-card {
display: grid;
grid-template-rows: auto 1fr auto;
gap: 12px;
}
.product-card img {
aspect-ratio: 4 / 3;
object-fit: cover;
}
This layout adapts from 1 to 5 columns without any media query. The aspect-ratio property keeps images proportional at every size. The card's row-based internal grid keeps the button at the bottom regardless of description length.
When You Still Need Media Queries
Not everything can be done without breakpoints:
- Typography shifts: Changing from 16px body to 20px body at wider viewports
- Navigation patterns: Switching from hamburger menu to horizontal nav
- Content decisions: Showing or hiding secondary sections
The goal is not to eliminate media queries entirely — it is to reduce them to the few places where they genuinely add value.
Key Takeaways
repeat(auto-fill, minmax(N, 1fr))creates responsive columns with zero breakpointsauto-fitcollapses empty tracks;auto-fillpreserves them- Use
min(N, 100%)insideminmax()to prevent overflow on small screens - Container queries give components their own responsive behavior
- Reserve media queries for typography and navigation — not column layout
Try It Yourself
Experiment with responsive grid columns using our CSS Grid Generator. Set columns to auto-fill with minmax and watch the layout adapt as you resize.