Fluid Layouts Without Breakpoints
Media queries change a layout at fixed points. Between those points, nothing adapts, and every new breakpoint is one more rule to keep in step with the others. The best responsive layouts need very few, because their parts stretch and shrink on their own. A handful of CSS functions make sizes fluid, a single grid line decides its own number of columns, and container queries let a component respond to the space it was actually given.
Think of a folding table with extra leaves. It opens out to fit the room it is in, and nobody has to measure the room first or keep a list of room sizes. Fluid CSS works the same way: you describe the limits, and the browser fits the layout to the space.
min, max and clamp
Three CSS functions choose a value for you. min() takes a list of values and uses the smallest, max() uses the largest, and clamp() takes three: a minimum, a preferred value and a maximum, and keeps the preferred value between the other two.
main {
width: min(100%, 60rem);
margin-left: auto;
margin-right: auto;
}
This gives the main element whichever is smaller: the full width available, or 60 rem. On a phone, 100 percent is smaller, so the content fills the screen. On a wide monitor, 60 rem is smaller, so the content stops growing at a comfortable width, and the two automatic margins share the leftover space equally, centring it. One line replaces what would otherwise be a default and a media query.
Fluid Type, Safely
A large heading that looks right on a laptop can take up half a phone screen. One idea is to size it in vw, where one vw is one percent of the window's width, so the heading grows and shrinks with the window. On its own, that breaks something important: when a reader zooms the page with the browser's zoom controls, a size in vw stays the same, because zooming does not change the proportions of the window. Text that cannot be enlarged fails people with low vision, which Chapter 12 measures properly.
The safe version mixes the two kinds of unit and puts limits on the result.
h1 {
font-size: clamp(1.75rem, 1.2rem + 2vw, 3rem);
}
Read it from the middle out. The preferred size is 1.2 rem plus 2 percent of the window's width, so it grows smoothly as the window widens. The first value is the floor: the heading is never smaller than 1.75 rem. The last value is the ceiling: it is never larger than 3 rem. On a phone the heading sits at its floor, on a laptop it is somewhere in between, and on a wide monitor it stops at its ceiling. Because the rem part follows the reader's settings and the limits are in rem, zooming in still makes the heading bigger.
A Grid That Counts Its Own Columns
The site's project grid currently has two rules: one column by default, and a media query that switches to three at 40 rem. Grid can make that decision by itself.
.project-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
gap: var(--space-l);
}
Replace the .project-grid rule and delete the width query from the last topic that went with it; the dark-theme query stays. Instead of a number, the first argument to repeat() is the keyword auto-fit: make as many columns as will fit. Each column is at least 16 rem wide and otherwise takes an equal share. On a phone, only one 16 rem column fits, so the cards stack. On a typical tablet, two fit, and the third card starts a second row. On a laptop there is room for three side by side.
There is no breakpoint anywhere, and the layout is right at every width in between, not only at the ones someone thought to test. The word fit matters too: with only three cards and room for five columns, auto-fit lets the three cards stretch across the whole row instead of leaving two empty columns at the end, which is what its sibling keyword, auto-fill, would do.
One refinement you will meet in other people's code guards against a container narrower than the minimum itself. Writing the minimum as min(16rem, 100%) lets a column shrink below 16 rem when the whole grid is narrower than that, so a very small screen never scrolls sideways.
Container Queries
A media query asks about the window. That is the wrong question for a component that can appear in different places. A card in a narrow sidebar and the same card in a wide main column share one window, but they have very different amounts of room. A container query asks the element the card lives in instead. Container queries have been available in every major browser since 2023 and counted as widely available since 2025.
<div class="card-holder"> <article class="card"> <h2>Opening times</h2> <p>Monday to Friday, nine until five.</p> </article> </div>
.card-holder {
container-type: inline-size;
}
@container (min-width: 30rem) {
.card {
display: flex;
gap: 1rem;
}
}
The first rule marks the holder as a container: an element whose width its descendants can ask about. The second is written like a media query, but with @container: when the nearest container is at least 30 rem wide, the card inside becomes a flex row. Put one copy of this holder in a narrow 12 rem sidebar and another in a wide column beside it. In the sidebar, the card's heading sits above its text, as usual. In the wide column, the heading and the text sit side by side. Same window, same CSS, two shapes.
The query always styles what is inside the container, never the container itself, which is why the card needs a holder around it. The holder's own width must come from the layout around it, as a block or a grid cell gets its width: a container ignores its own content when sizing itself, so a holder that would normally shrink to fit its content collapses to zero width.
- "More breakpoints make a page more responsive." Fewer, better-chosen rules usually adapt better. A fluid size or an auto-fitting grid is right at every width, not only at the breakpoints.
- "Sizing text in
vwis the modern way." On its own it ignores zoom, so readers who enlarge text cannot. Combine it withreminsideclamp(). - "Media queries and container queries do the same thing." A media query asks about the window; a container query asks about the space a component was given. The second is what makes a component reusable in different places.
- The site's card grid now works at every width with one line and no breakpoint to maintain.
- Components that respond to their container can be dropped into a sidebar, a main column or a footer and adapt without new CSS.
Knowledge Check
A heading has font-size: clamp(1.5rem, 1rem + 2vw, 3rem). What happens on a very wide screen?
- It keeps growing with the window, without limit
- It stops growing at 3rem, the value at the end
- It drops back to 1.5rem, the value at the start
- It stays at the middle value, whatever the screen
What does repeat(auto-fit, minmax(16rem, 1fr)) do?
- It always makes exactly sixteen equal columns
- It limits the grid to one row of 16rem cards
- It makes as many 16rem-or-wider columns as fit
- It adds a breakpoint to the page every 16rem
Why is font-size: 5vw on its own a problem for some readers?
- Zooming in does not make the text any bigger
- Only some browsers understand the vw unit
- It makes the text change as the reader types
- The text becomes too large on small phones
What does a container query ask about?
- The width of the whole browser window
- The model of phone the page is shown on
- The size of the stylesheet that holds it
- The width of the element around the component
You got correct