You add this to your Grid:
grid-template-columns:
repeat(auto-fit, minmax(220px, 1fr));
Everything suddenly looks more responsive. Great.
Then you change 220px to 300px and the layout starts overflowing. Or you make it 150px and your cards become tiny. Or you copy the same line into another project and it behaves completely differently.
That's usually the point where minmax() stops feeling clever and starts feeling like magic CSS you're afraid to touch.
It isn't magic. Those two values are simply telling the browser how small a Grid track is allowed to become and how large it is allowed to grow. Once you can see that happening, minmax() becomes much easier to reason about.
Change the minimum width and watch when the Grid decides there is no longer room for another column.
1. Your cards keep getting too narrow
You've made a responsive Grid. Technically, it works. The cards get smaller as the screen gets smaller. The problem is that eventually they get too small to be useful.
Consider:
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
On a wide screen, those three columns may look great. Make the container narrower and each column simply gets a smaller share of the available width. Nothing in that CSS says stop shrinking when the cards become unreadable.
1fr distributes available space. It does not know what you consider a sensible minimum width for the content.
Give the tracks a lower limit with minmax() so they never shrink past a useful width.
.grid {
display: grid;
grid-template-columns:
repeat(3, minmax(220px, 1fr));
gap: 20px;
}
Now you're saying: each track should be at least 220px wide, and if there is extra space, it can grow. That is the basic job of minmax().
minmax() can protect the width of a track. It does not automatically decide how many tracks should exist.
2. Minmax doesn't automatically make the number of columns responsive
This is where a lot of copied CSS gets misunderstood. You can write:
grid-template-columns:
repeat(4, minmax(220px, 1fr));
and still have a layout that breaks on a narrow screen. Why? Because you've still told Grid to make four columns. Each of those columns now wants to be at least 220px wide. Four tracks need at least 880px before gaps are included. minmax() is doing exactly what you asked - the problem is the fixed column count.
You've made the track size flexible but left the number of tracks fixed.
For repeated content such as cards or gallery items, combine minmax() with auto-fit.
.grid {
display: grid;
grid-template-columns:
repeat(auto-fit, minmax(220px, 1fr));
gap: 20px;
}
Now Grid can make two decisions: how wide should each track be (at least 220px, with room to grow), and how many tracks should fit (as many as the available space can hold). That's why this pattern appears so often in responsive CSS Grid examples.
3. Your minimum is bigger than the space available
Now we get to one of the most confusing minmax() problems. Imagine this:
grid-template-columns:
repeat(auto-fit, minmax(300px, 1fr));
Your card should never be smaller than 300px. Makes sense. Then the Grid is displayed inside a container that is only 260px wide. The track still wants its 300px minimum. The container only has 260px. That can result in the Grid becoming wider than its container.
A minimum is a real constraint. minmax(300px, 1fr) means 300px is the minimum track size, not "try for it unless things get awkward."
Let the minimum respect the available width if the component may become narrower than your preferred minimum.
.grid {
grid-template-columns:
repeat(
auto-fit,
minmax(min(100%, 300px), 1fr)
);
}
Now the track can still aim for a 300px minimum when the space exists. But when the entire container is narrower than 300px, it can respect 100% of that available width instead.
4. The second value controls how much the track can grow
Most examples use minmax(220px, 1fr). The first value gets most of the attention. But the second value matters too. Here, 1fr means the track can grow and take a share of the available free space. So if three tracks fit into the container with room left over, they don't have to remain exactly 220px wide - they can expand. That's why a Grid like this can fill the row instead of leaving awkward unused space after the final item.
A lot of developers mentally read 220px as the card width. It isn't - it is the minimum track size.
Think of the rule as "at least this wide" and "allowed to grow this far."
Minimum ≠ fixed width.
5. Auto-fit and auto-fill are not quite the same
If you've searched for responsive Grid examples, you've probably seen both repeat(auto-fit, minmax(220px, 1fr)) and repeat(auto-fill, minmax(220px, 1fr)). They often look identical when there are enough items to fill the row. The difference becomes easier to see when the container has room for more tracks than you actually have content for.
- auto-fill can preserve space for additional tracks that could fit.
- auto-fit collapses those unused tracks so the existing items can use the available space.
For many responsive card layouts, auto-fit gives the result people expect because the existing cards can stretch to fill the row. But neither is universally "better" - the right choice depends on whether you want the empty track space to remain part of the layout.
6. Minmax isn't only for card layouts
Responsive cards are the example everyone remembers. But minmax() is useful anywhere you want a Grid track to have boundaries rather than a single fixed size. For example, imagine a sidebar that should be useful without becoming enormous.
.layout {
display: grid;
grid-template-columns:
minmax(180px, 260px) 1fr;
}
The sidebar can live within a sensible range while the main content takes the remaining space. The useful question is: does this track need one exact size, or does it need a sensible range? If it needs a range, minmax() is worth considering.
The three minmax patterns worth remembering
You don't need to memorise dozens of combinations. These three cover a lot of everyday Grid work.
Responsive cards
grid-template-columns:
repeat(auto-fit, minmax(220px, 1fr));
Useful when the number of columns should respond to available space.
Minimum that cannot exceed the container
grid-template-columns:
repeat(
auto-fit,
minmax(min(100%, 300px), 1fr)
);
Useful when a component may become narrower than your preferred card width.
Bounded sidebar
grid-template-columns:
minmax(180px, 260px) 1fr;
Useful when a track should have both a sensible lower and upper limit.
Copying minmax(220px, 1fr) works until your layout needs something slightly different. Then the important thing isn't remembering the magic numbers. It's understanding the instruction: how small can this track become, and how large can it grow? Once those two answers make sense, changing minmax() stops feeling like trial and error.
If your Grid still looks wrong
Check these before changing random values.
- Is your minimum too small? Your cards may technically fit but become difficult to use.
- Is your minimum too large? The Grid may overflow when its container becomes narrower.
- Is the number of columns still fixed? minmax() does not automatically change repeat(4, ...) into fewer columns.
- Are you using auto-fit or auto-fill intentionally? They can behave differently when the row has empty space.
- Is the problem actually inside the Grid item? An image, long URL or nested element may be forcing the track wider.
You can learn a lot about minmax() by reading the syntax. You'll understand it much faster when you can change the values and watch the Grid react. Use Gridlock Holmes to experiment with your track sizes visually and see the CSS change with the layout.