Media queries ask the viewport how wide it is. But a component often sits in a sidebar, a modal or a column, where the viewport tells you very little. Intrinsic layouts solve that by letting Grid and Flexbox decide how many items fit, based on the space actually available.
The auto-fitting grid
This is the most useful line of responsive CSS you will learn:
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
gap: 1rem;
}
Read it from the inside out:
minmax(15rem, 1fr): each column is at least15remwide, and can grow to share the free space equally.repeat(auto-fit, ...): create as many columns as will fit at that minimum width.
In a container 50rem wide, three columns of at least 15rem plus two 1rem gaps fit, so you get three. At 32rem, two fit. Below 31rem, one column. No breakpoints, and the grid works whether it is in the main content area or a narrow sidebar.
<ul class="grid">
<li class="card">Planning</li>
<li class="card">Design</li>
<li class="card">Build</li>
<li class="card">Launch</li>
</ul>
Fixing overflow on very narrow screens
There is one catch. If the container is narrower than 15rem, the single column still demands 15rem and overflows. On a 320 pixel phone with padding, or at high zoom, that causes horizontal scrolling.
Wrap the minimum in min():
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
gap: 1rem;
}
Now the minimum is 15rem or the full container width, whichever is smaller. The column can never be wider than its container.
Make minmax(min(100%, X), 1fr) your default pattern for card grids. It handles every width from a zoomed-in phone to a wide monitor.
auto-fit versus auto-fill
Both keywords create as many tracks as fit. The difference shows when there are fewer items than tracks.
| Keyword | Empty tracks | Result with 2 items in a wide container |
|---|---|---|
auto-fit | Collapsed to zero width | The 2 items stretch to fill the row |
auto-fill | Kept as empty columns | The 2 items stay narrow, with empty space beside them |
Use auto-fit when items should fill the row. Use auto-fill when you want items to keep a consistent size, for example so the last row of a product grid matches the rows above.
Wrapping flex layouts
Flexbox is better when items have different natural widths, such as tags, buttons or form controls. Turn on wrapping and give items a flexible basis:
.cluster {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1rem;
}
<ul class="cluster">
<li><a href="/html">HTML</a></li>
<li><a href="/css">CSS</a></li>
<li><a href="/responsive">Responsive design</a></li>
<li><a href="/accessibility">Accessibility</a></li>
</ul>
Items sit on one line while they fit and wrap onto new lines when they do not. Each keeps its natural width.
Equal-width items that wrap
For items that should share the space evenly, set flex with a basis:
.switcher {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.switcher > * {
flex: 1 1 12rem;
}
flex: 1 1 12rem means "start at 12rem, grow and shrink as needed". Items share a row while each can be at least about 12rem, then wrap. Unlike Grid, a wrapped item on the last line grows to fill that line, which is ideal for button groups and form rows.
The sidebar layout
A classic layout is a sidebar beside the main content on wide screens, and stacked on narrow ones. Flexbox can do this without a breakpoint:
<div class="with-sidebar">
<aside class="sidebar">Filters</aside>
<main class="content">Results</main>
</div>
.with-sidebar {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.sidebar {
flex-basis: 16rem;
flex-grow: 1;
}
.content {
flex-basis: 0;
flex-grow: 999;
min-inline-size: 60%;
}
How it works:
- The content has a huge
flex-grow, so it takes almost all the free space, leaving the sidebar at its16rembasis. min-inline-size: 60%says the content must be at least 60 percent of the container.- When the container gets too narrow for both, the content cannot reach 60 percent, so the items wrap. Both then fill the full width.
The switch happens based on the container's width, not the viewport's, so the same component works anywhere on the page.
In this pattern the sidebar comes first in the HTML and appears above the content when stacked. If the main content should come first for readers, put it first in the HTML too. Do not use order or flex-direction: row-reverse to change the visual order alone, because keyboard and screen reader users follow the HTML order.
The centred container
Most pages need a content column that fills small screens and stops at a comfortable width:
.container {
width: min(100% - 2rem, 70rem);
margin-inline: auto;
}
On a phone the container is the full width minus 1rem on each side. On wider screens it stops at 70rem and centres. One line replaces the usual max-width plus padding plus media query combination.
Preventing blow-outs
Two default behaviours cause most "the layout overflows on mobile" bugs in Grid and Flexbox.
Flex items will not shrink below their content. A flex item's default min-width is auto, so a long URL or wide code block can push it past the container. Allow shrinking:
.content {
min-width: 0;
}
Grid's 1fr has the same minimum. 1fr means minmax(auto, 1fr), so wide content forces the track wider. Use an explicit zero minimum:
.layout {
display: grid;
grid-template-columns: minmax(0, 1fr) 16rem;
}
Lesson 9 covers wide content such as tables and code in more detail.
When you still need media queries
Intrinsic layouts handle a lot, but not everything. Media queries are still the right tool when:
- The layout must change in ways that wrapping cannot express, such as moving the navigation from the top to the side.
- You want to change typography, spacing or visibility at a specific width.
- You are responding to user preferences or input type.
And when a component needs to change its internal design based on its own width, rather than just wrap, container queries are the answer. You will meet them in lesson 11.
Summary
repeat(auto-fit, minmax(min(100%, 15rem), 1fr))creates a grid that fits as many columns as space allows, without overflowing.auto-fitstretches items to fill the row;auto-fillkeeps empty tracks.flex-wrap: wrapwith aflex-basiscreates rows that wrap when items no longer fit.- The sidebar pattern uses
flex-growandmin-inline-sizeto stack based on the container's width. min-width: 0andminmax(0, 1fr)prevent wide content from blowing out a layout.
In the next lesson you will decide where breakpoints belong when you do need them.