CSS grid generator
Set the columns and rows, then drag across the cells to place an item — or start from a preset. The CSS updates
as you go — copy it with line numbers or as grid-template-areas, together with the HTML or as Tailwind CSS classes.
For cards that wrap on their own, switch to Auto-fill cards.
Drag across empty cells (or tap one) to add an item. Edit or remove items in the list.
Use 1fr, 200px, 25%, auto, min-content or minmax(100px, 1fr).
Overlapping items can't be written as grid-template-areas, so the CSS uses line numbers.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 16px;
}
.header {
grid-area: 1 / 1 / 2 / 4;
}
.sidebar {
grid-area: 2 / 1 / 4 / 2;
}
.main {
grid-area: 2 / 2 / 3 / 4;
}
.footer {
grid-area: 3 / 2 / 4 / 4;
} <div class="grid"> <div class="header"></div> <div class="sidebar"></div> <div class="main"></div> <div class="footer"></div> </div>
<div class="grid grid-cols-[repeat(3,_1fr)] grid-rows-[repeat(3,_1fr)] gap-4"> <div class="col-start-1 col-end-4 row-start-1 row-end-2"><!-- header --></div> <div class="col-start-1 col-end-2 row-start-2 row-end-4"><!-- sidebar --></div> <div class="col-start-2 col-end-4 row-start-2 row-end-3"><!-- main --></div> <div class="col-start-2 col-end-4 row-start-3 row-end-4"><!-- footer --></div> </div>
How CSS grid placement works
A grid is a set of numbered lines. Three columns have four column lines: line 1 is the left edge, line 4
the right edge. An item says which lines it starts and ends on, so grid-area: 2 / 1 / 4 / 2 means
row line 2 / column line 1 / row line 4 / column line 2 — rows 2 and 3 of the first column. That is the order
the shorthand uses: row-start, column-start, row-end, column-end.
- fr shares out the space left after fixed sizes and gaps. In a 600px-wide grid with
200px 1fr 1frand 16px gaps, each1frcolumn is (600 − 200 − 32) ÷ 2 = 184px. - gap only goes between tracks, never around the outside.
gap: 8px 24pxis the row gap first, then the column gap. - grid-template-areas draws the layout as text: each quoted string is a row, each name a cell, and
.an empty cell. Every area has to be a rectangle and areas can't overlap — when yours do, this page falls back to line numbers. - repeat(3, 1fr) is the same as writing
1fr 1fr 1fr. The output uses it whenever every track is the same size. - repeat(auto-fill, minmax(200px, 1fr)) fits as many columns as the container has room for: the browser
counts how many 200px columns plus their gaps fit, then
1frshares out what is left. In a 600px container with 16px gaps that is 2 columns (3 would need 632px), each (600 − 16) ÷ 2 = 292px wide.
The presets
- Holy grail — a header and footer across the top and bottom, with a 120px navigation column, the main
content and a 120px side column between them:
120px 1fr 120px. - 12-column — twelve equal
1frcolumns with 16px gutters, the layout most CSS frameworks use; the main area spans 8 columns and the side area 4. - Card wall — the auto-fill mode: 200px-minimum cards in 120px rows that wrap to as many columns as fit.
How we test it
The CSS, HTML and Tailwind output is pasted, exactly as you would copy it, into Chrome for 304 different
grids at a phone and a desktop size, and every item's position and size is measured (1,652
item boxes). Grids made of pixel and fr tracks must match the track maths above to half a pixel; the named-areas
CSS must lay out exactly like the line-number CSS; and the Tailwind classes, compiled with Tailwind CSS 4.3.3,
must lay out exactly like the plain CSS. Each preset must render the layout its description above promises, at three sizes.
The auto-fill mode is checked the same way: 141 settings at 4 container
widths (7,124 card boxes) must match the column-counting rule above, and the Tailwind
version must match the CSS.
Questions
Why is 1fr sometimes wider than I set it?
1fr never shrinks below its content's minimum width. A long word or a wide image keeps the column wide. Use
minmax(0, 1fr) to let it shrink to nothing.
Line numbers or named areas — which should I use?
Named areas are easier to read and to rearrange in a media query: you change one grid-template-areas block
and every item moves. Line numbers work for anything, including items that overlap on purpose.
How do I make the grid responsive?
Put a different grid-template-columns (and, with named areas, a different grid-template-areas)
inside a @media rule. For cards that wrap on their own, switch to Auto-fill cards: it writes
repeat(auto-fill, minmax(200px, 1fr)) with your minimum width, and needs no media query at all.
auto-fill or auto-fit — what's the difference?
Both fit as many columns as there is room for. When there are fewer cards than columns, auto-fill keeps the
empty columns, so the cards stay their normal width; auto-fit collapses the empty columns to zero, so the
cards stretch across the whole row. With enough cards to fill a row they look the same.
What do the Tailwind classes need?
Tailwind CSS v4 or v3.3+. Track sizes are arbitrary values in square brackets (spaces become underscores), gaps use the
spacing scale where the pixel value is a multiple of 4, and each item gets col-start, col-end,
row-start and row-end.
Shaping the items too? Try the clip-path generator, or turn existing CSS into classes with the CSS to Tailwind converter.