Topic 8.2
repeat, minmax and Auto Tracks
In one line
repeat() writes many tracks at once, minmax() gives a track a smallest and largest size, auto-fill and auto-fit let the grid choose how many columns fit, and grid-auto-rows, grid-auto-columns and grid-auto-flow control the extra tracks the grid creates on its own.
Think of it like this
Seating guests at a party. You tell the host: "each chair needs at least 1 metre of table, and chairs share any extra space". The host fits as many chairs as the table allows, so a long table gets more chairs. When more guests arrive than you planned, the host sets up extra tables of a standard size. minmax() is the "at least 1 metre" rule, auto-fit is "as many chairs as fit", and the extra tables are the implicit grid.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- `repeat()`
- A function that writes the same track (or pattern of tracks) many times.
- `minmax()`
- A function for one track's size: never smaller than the first value, never bigger than the second.
- `auto-fill`
- Inside
repeat(): make as many tracks as fit, and keep empty ones. - `auto-fit`
- Inside
repeat(): make as many tracks as fit, then collapse the empty ones so items can stretch. - Explicit grid
- The rows and columns you wrote in
grid-template-rowsandgrid-template-columns. - Implicit grid
- Extra rows or columns the browser adds when items don't fit the explicit grid.
- `grid-auto-rows`
- The size of rows the browser adds by itself.
grid-auto-columnsdoes the same for columns. - `grid-auto-flow`
- Whether auto-placement fills rows or columns first, and whether it back-fills holes (
dense).
Step by step
01Shorten a long template with repeat()
Twelve equal columns written by hand are hard to read. repeat(12, 1fr) says the same thing in one word.
.page {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 16px;
}02Give each card a minimum width
repeat(3, minmax(200px, 1fr)) makes three columns that share the space but never go below 200px. On a narrow screen that's still three columns of 200px, so the grid overflows. A fixed count can't adapt.
.cards {
display: grid;
grid-template-columns: repeat(3, minmax(200px, 1fr));
gap: 16px;
}03Let the browser count the columns
Replace 3 with auto-fit. Now the browser asks: how many 200px columns (with gaps) fit? On a 900px screen, 4; on a 450px screen, 2. Then the 1fr maximum stretches them to fill the row.
Wrap the minimum in min() so a container narrower than 200px gets one full-width column instead of overflowing: minmax(min(200px, 100%), 1fr).
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
gap: 16px;
}04Choose auto-fit or auto-fill
With only two cards in a wide row, auto-fill keeps the empty column slots, so the two cards stay small on the left. auto-fit collapses the empty slots, so the two cards stretch across the row.
05Size the rows the grid adds
You defined columns but no rows, so every row is implicit. grid-auto-rows: minmax(120px, auto) makes each one at least 120px tall but lets it grow when a card has more text.
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-auto-rows: minmax(120px, auto);
gap: 16px;
}06Change the flow direction or pack holes
grid-auto-flow: column fills top to bottom and adds columns instead of rows. grid-auto-flow: row dense lets small items jump back into holes left by wide ones. Use dense only when the order of items doesn't matter (a photo wall), never for content people read in sequence.
.photos {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-flow: row dense;
}
.photos .wide {
grid-column: span 2;
}Try it yourself
- 1
Predict the column count
In the first example, change both minimums from
100pxto140px. Predict how many tracks fit in 460px with a 20px gap: (460 + 20) / (140 + 20) = 3. Then predict the.fillitem width: (460 − 40) / 3 = 140. Run and check. - 2
Fill the auto-fill grid
In the first example, add two more
<div>s to both grids so each has four items. Predict the console: both grids now print the same tracks and widths, because no track is empty. - 3
Let the rows grow
In the second example, change
grid-auto-rowstominmax(40px, auto)and put a long sentence in item 5. Predict which row grows (row 2 only) and check therows:line.
Code & diagrams
Both grids fit 4 tracks: (460 + 20) / (100 + 20) = 4. `auto-fill` keeps the two empty tracks, so each item is 100px. `auto-fit` collapses them to 0px, so the two items share the whole row: (460 − 20) / 2 = 220px.
Preview
Console
Nothing logged yet.Expected console output
.fill tracks: 100px 100px 100px 100px
.fill item width: 100
.fit tracks: 220px 220px 0px 0px
.fit item width: 220You defined one 60px row. Items 4 to 7 need two more rows, so the browser adds them at the `grid-auto-rows` size. The computed value lists implicit tracks too.
Preview
Console
Nothing logged yet.Expected console output
columns: 96px 96px 96px
rows: 60px 40px 40pxItem 3 needs two columns and can't fit in row 1's last cell, so it moves to row 2. Without `dense`, item 4 follows it (x=200, y=40) and the hole stays. With `dense`, item 4 goes back to the hole (y=0). In the column grid, items fill down two rows, then start a new 80px column.
Preview
Console
Nothing logged yet.Expected console output
.sparse item 4 at x=200 y=40
.dense item 4 at x=200 y=0
.col item 3 at x=80 y=0
.col item 5 at x=160 y=0Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Use auto-fill with a plain fr
Write grid-template-columns: repeat(auto-fill, 1fr);.
Break #2
A minimum that is too big for small screens
Use repeat(auto-fit, minmax(400px, 1fr)) and view the page on a 360px-wide phone.
Break #3
Dense packing breaks reading order
Add grid-auto-flow: dense to a list of numbered steps where some steps span two columns.
Myth vs fact
Myth
auto-fill and auto-fit are the same thing.
Fact
They only differ when there are fewer items than tracks: auto-fill keeps the empty tracks, auto-fit collapses them so the items stretch.
Myth
You always need media queries to change the number of columns.
Fact
repeat(auto-fit, minmax(…, 1fr)) changes the column count by itself as the container gets wider or narrower. Media queries (Phase 9) are for bigger changes, like rearranging areas.
Myth
Items beyond the rows you defined are hidden or overflow.
Fact
The grid adds implicit rows (or columns) for them, sized by grid-auto-rows / grid-auto-columns.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
The repetition count for
auto-fill/auto-fituses the track's max sizing function if it is definite, otherwise the min. Sominmax(100px, 1fr)counts with 100px, butminmax(100px, 200px)counts with 200px. If the container has only amax-width, that is used; if its size is indefinite, you get one repetition. - ▸
grid-template-columnsandgrid-template-rowsare animatable between track lists of the same length in all current browsers, sogrid-template-rows: 0fr→1fris a popular way to animate an accordion's height to its content without measuring it in JavaScript. - ▸
grid-auto-rowsandgrid-auto-columnsaccept a list that repeats:grid-auto-rows: 100px 200pxmakes implicit rows alternate 100px, 200px. Implicit tracks before the explicit grid (created by negative placement) are sized from the end of the list backwards. - ▸
Each
repeat()in a template can only useauto-fill/auto-fitonce, and that repeat may not containfror content-sized tracks (auto,min-content) on their own. Any other tracks beside it must also have fixed sizes:200px repeat(auto-fill, 120px)is valid,1fr repeat(auto-fill, 120px)is not.
Remember this
- 1
**
repeat(count, tracks)** saves typing:repeat(4, 1fr)is the same as1fr 1fr 1fr 1fr. The tracks part can be a pattern:repeat(3, 100px 1fr)makes six tracks that alternate. You can mix it with other tracks:200px repeat(3, 1fr) 200px. - 2
**
minmax(min, max)** makes one track that is never smaller thanminand never larger thanmax:minmax(150px, 1fr)is at least 150px and grows to share free space.minmax(100px, auto)is at least 100px and grows to fit its content.fris only allowed as the max; and ifminis bigger thanmax, themaxis ignored.minmax(0, 1fr)is the fix for blowouts you saw in Topic 8.1. - 3
**
auto-fillandauto-fit** replace the number inrepeat():repeat(auto-fill, minmax(150px, 1fr))means "as many columns as fit, each at least 150px". The browser divides the container width (plus one gap) by the minimum track size plus a gap and rounds down. Then the1frmaximum spreads the leftover across the columns. This one line gives a responsive gallery with no media queries: 4 columns on a wide screen, 2 on a phone. - 4
The two keywords differ only when there are fewer items than columns.
auto-fillkeeps the empty columns, so a few items stay at their small size and leave empty space on the right.auto-fitcollapses the empty columns to 0 width (and the gaps around them), so thefrmaximum lets the real items stretch across the whole row. With plenty of items, the two look identical. Note thatauto-fillandauto-fitneed a definite size inside:repeat(auto-fill, 1fr)is invalid, because the browser can't count how many1frtracks fit. - 5
The tracks you write in
grid-template-*form the explicit grid. When items need more room (more items than cells, or an item placed on line 7 of a 3-column grid), the browser adds tracks to the implicit grid. Their size comes from **grid-auto-rowsandgrid-auto-columns**, which default toauto(fit the content).grid-auto-rows: minmax(120px, auto)is a favourite: every new row is at least 120px tall but can grow for long content. - 6
**
grid-auto-flow** chooses how auto-placement fills the grid.row(the default) fills each row and adds rows.columnfills each column top to bottom and adds columns, sized bygrid-auto-columns. Adding **dense** (grid-auto-flow: row dense) lets later small items go back and fill holes left by earlier big items. That packs the grid tightly, but the visual order no longer matches the HTML order, which confuses keyboard and screen-reader users who move through the page in HTML order.
Explain it without notes
What does minmax(200px, 1fr) mean for one track?
Explain the difference between auto-fill and auto-fit, and when you can see it.
What is the implicit grid, and which properties size it?
What does grid-auto-flow: dense do, and what is its accessibility risk?
Practice
Write a gallery grid where every card is at least 180px wide, cards share any extra width, and there's a 16px gap, with no media queries.
A grid has grid-template-columns: repeat(4, 1fr) and 10 items. You want every row to be at least 150px tall but taller when needed. Write the CSS.
Write a grid that lays out its items in columns of three, top to bottom, adding new 120px-wide columns to the right as needed.
Trade-offs
- ↔
auto-fitgalleries adapt without media queries, but you no longer pick the exact column count at each width; when a design needs "exactly 2 columns on tablets", use a fixedrepeat()inside media or container queries. - ↔
densepacking removes holes but sacrifices order; keep it for decorative collections.
Done when you can
I can write long track lists with
repeat()and give tracks limits withminmax().I can build a responsive gallery with
repeat(auto-fit, minmax(min(…, 100%), 1fr)).I can predict when
auto-fillandauto-fitlook different.I can size implicit rows and columns with
grid-auto-rowsandgrid-auto-columns.I can switch the flow to columns and explain the risk of
dense.