Grid is the layout model for two dimensions at once: rows and columns together. The first thing to learn is the fr unit, which divides the available space between tracks, and the second is the difference between a track that is fixed and one that is a fraction of what is left.
What does one fr mean?
One fraction of the space left over after the fixed things have been accounted for. That last part matters: fractions divide what remains, not the whole container.
.layout {
display: grid;
grid-template-columns: 2fr 1fr; /* two thirds and one third */
}
In a container of 900 pixels, the first column gets 600 and the second 300. But if you add a fixed track alongside them, the fractions divide only what is left over.
.layout {
display: grid;
grid-template-columns: 20rem 1fr 1fr;
}
Here the 20rem sidebar takes its width first, and the two flexible columns split the remainder equally. A fixed track and a fraction can be mixed freely in the same list, and the order of the tracks is the order of the columns in the markup.
What other track sizes are there?
.layout {
display: grid;
grid-template-columns:
240px /* a fixed width */
25% /* a percentage of the container */
1fr /* a fraction of what is left */
minmax(200px, 1fr) /* at least 200px, share the rest */
auto; /* as wide as the content needs */
}
minmax() is the one that solves real problems. It sets a floor and a ceiling for a track, so minmax(200px, 1fr) means "at least 200 pixels wide, and share the remaining space with the other flexible tracks". It is the standard way to write a grid that does not collapse on a narrow screen.
auto sizes the track to its content, and auto tracks also absorb leftover space when there is no flexible track to take it. A grid of auto columns therefore looks like it fills the width, which is a common surprise.
What does repeat() do?
It writes a pattern instead of a list. This is the same as writing twelve copies of 1fr.
.grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 1rem;
}
.card {
grid-column: span 4; /* one third of twelve columns */
}
The twelve-column grid is a convention rather than a requirement, but it is a good one, because it lets an item span a fraction of the page with a single number that stays meaningful at different sizes. A card that spans 4 of 12 columns is a third of the width, and a card that spans 6 is a half.
repeat() also handles auto-fill and auto-fit, which decide how many tracks to create.
/* as many 16rem columns as fit */
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
gap: 1rem;
}
What is the difference between auto-fill and auto-fit?
Both create as many tracks as fit. The difference is what happens to the empty space when there are fewer items than tracks.
/* auto-fill: keeps the empty tracks, so the items stay narrow */
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
/* auto-fit: collapses the empty tracks, so the items stretch */
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
With three cards in a container that fits four columns, auto-fill leaves the fourth track empty and the cards stay 16rem wide, aligned to the left. auto-fit collapses the empty track and the three cards stretch to fill the width. Which one you want depends on whether the cards should keep a maximum width or fill the row.
How do items get placed?
Automatically, in order, filling each row before starting the next. You can take control with the grid line properties, and the shorthand is the clearest way to express it.
.header { grid-column: 1 / -1; } /* from the first line to the last */
.sidebar { grid-column: 1; grid-row: 2 / 4; }
.main { grid-column: 2 / 4; grid-row: 2; }
The -1 refers to the last line of the explicit grid, so 1 / -1 always spans the full width regardless of how many columns there are. This is the standard way to make a header span the page.
How do you let the grid work out its own tracks?
Not every grid needs fixed columns. The automatic keywords let the grid decide how many tracks fit and how wide they should be, which is what makes a grid layout responsive without a single media query.
repeat(auto-fill, ...) creates as many tracks as fit. The track size is usually a minimum, and the tracks share the leftover space. repeat(auto-fit, ...) does the same thing but collapses the empty tracks afterwards, so a single item in a three-track grid stretches across the whole row instead of sitting in the first third.
.gallery {
display: grid;
/* as many columns as fit, each at least 14rem, sharing the space */
grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
gap: 1rem;
}
/* the same grid, but a lone item fills the row */
.gallery--wide {
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
}The minmax() function is doing the important work in both. Its first argument is the smallest the track may be, and its second is the largest it may grow to. Pairing a rem minimum with a 1fr maximum gives tracks that never shrink below a usable width and grow to share whatever space is left.
For rows, the equivalent is grid-auto-rows, which sets the size of the rows the grid creates implicitly. Combine it with minmax() and the rows grow with their content instead of being a fixed height.
.tiles {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
/* implicit rows are at least this tall, and grow if the content needs more */
grid-auto-rows: minmax(6rem, auto);
gap: 0.75rem;
}One thing to keep in mind: an automatic track count is based on the available width, not on the content. A grid that fits three columns on a wide screen fits three on a narrow one too, just wrapped onto two lines, and each item keeps the same width. That is exactly what you want for cards, and exactly what you do not want for a layout whose columns should change shape at a breakpoint.
Quick reference
frdivides the space left after the fixed tracks are taken out.- Fixed, percentage,
autoandminmax()tracks can be mixed in one list. repeat(n, value)writes a pattern instead of a list.auto-fillkeeps empty tracks;auto-fitcollapses them.grid-column: 1 / -1spans the full width whatever the column count.- Items are placed automatically, row by row, unless you place them explicitly.
Try it yourself: build a three-column grid with a fixed 240-pixel first column and two flexible ones, and check in the developer tools that the fractions divide the remaining space. Then change the grid to repeat(auto-fit, minmax(16rem, 1fr)) and resize the window to watch the number of columns change without any media query.