CSS grid generator that checks grid-template-areas before you paste it
Define your columns and rows, set the row and column gaps separately, then paint the layout onto a map of cells and get back both the container rule and the child placement rules. Every track you type is checked against what CSS actually accepts, and every named area is checked against the rule that catches people out: an area has to be a filled rectangle, or the browser throws the entire grid-template-areas declaration away without a word in the console. This page names the offending cell instead.
- Nothing is uploaded or saved
- Works offline
Emitted as gap: 12px. A percentage gap resolves against the container’s own size on that axis, which is why it grows as the container does.
Pick a name, then click or drag across the cells. Each named area has to end up as a filled rectangle — that rule is checked below and it is the one CSS enforces silently.
.layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
gap: 12px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
.header {
grid-area: header;
}
.sidebar {
grid-area: sidebar;
}
.main {
grid-area: main;
}
.footer {
grid-area: footer;
}repeat(4, 1fr) and 1fr 1fr 1fr 1fr are the same declaration. The switch only changes how it reads.
The quoted block is one string per row, 2 cells wide. It is padded so the columns line up — CSS ignores the extra spaces, and it is the only way a person can read a map of this shape:
"header header"
"sidebar main"
"footer footer"
Related tools on this site: give the cards a layered shadow, fill an area with a gradient and ease the transition between two layouts.
How it works
- 1
Write the tracks the way you would in CSS
Each column and row is its own field and takes whatever CSS takes — 1fr, 240px, 25%, auto, min-content, minmax(160px, 1fr), fit-content(300px). Anything that is not a real track size gets a sentence explaining what is wrong with it rather than a red outline, because “200” and “200px” look identical at a glance and only one of them works.
- 2
Set the two gaps separately
Row gap and column gap are different controls here because they are different decisions. A layout of text cards usually wants more air between rows than between columns, and the single-value `gap` shorthand quietly forces them to match. The output collapses to one value when you do set them the same, so nothing is longer than it needs to be.
- 3
Paint the areas
Give each region a name, pick it up, and click or drag across the map. An empty cell is written as a single dot, which is how CSS spells “nothing goes here”. Renaming an area rewrites every cell holding it in one step, and deleting one clears its cells rather than leaving orphaned names behind in the output.
- 4
Read the validation, not just the preview
If an area is not a rectangle, the page says which area, which cell inside its bounding box breaks it, and what that cell holds instead — then leaves grid-template-areas out of the CSS entirely until it is fixed. Emitting a declaration that a browser is going to discard would be worse than emitting nothing, because in the browser it fails without an error and the layout simply falls back to automatic placement.
- 5
Copy both halves
The output is the container rule and one small rule for each named child, since grid-template-areas on its own places nothing — the children still need `grid-area: header` to claim their region. Turn on the repeat() switch to see identical neighbouring tracks collapsed into repeat(3, 1fr); it changes how the declaration reads and not what it does.
Frequently asked questions
What exactly makes grid-template-areas invalid?
Two rules, and both fail silently. Every row string must contain the same number of cells, and every named area must form a solid rectangle — all the cells inside its bounding box have to carry that name, with no gaps and no other area cutting into it. An L-shaped region, a name that appears in two disconnected places, or a row with one cell too few makes the whole property invalid, and an invalid property is dropped. Nothing is logged, no warning appears; the grid just falls back to automatic placement and your layout looks scrambled for no visible reason. Load the “A broken L-shape” preset above to see the exact error this page reports for it.
When should I use fr and when should I use a percentage?
fr divides up what is left over after everything else has been accounted for, including the gaps. A percentage is measured against the container’s own content box and knows nothing about the gaps at all. So `1fr 1fr` with a 20px gap gives two columns that exactly fill the row, while `50% 50%` with the same gap overflows it by 20px — the two halves plus the gap add up to more than the whole. Percentages are the right tool when you genuinely want a track measured against the container regardless of its neighbours; for “share the remaining space”, fr is what the unit was invented for.
Why will my 1fr column not shrink, even though the window is narrow?
Because 1fr is really minmax(auto, 1fr), and that auto minimum is the size of the content. A long unbroken string, a wide table or an image with no max-width sets a floor the track cannot go below, so the grid overflows its container instead of the column narrowing. The fix is to write the track as `minmax(0, 1fr)`, which sets the floor to zero and lets the content be squeezed — and then to make sure the content inside can actually cope, usually with overflow or min-width: 0 on the child. This is the most common “CSS Grid is broken” report there is, and it is not a bug.
What is the difference between auto-fit and auto-fill?
Both are used with repeat() to create as many tracks as fit — `repeat(auto-fill, minmax(200px, 1fr))` — and they only differ when there are fewer items than there is room for. auto-fill keeps the empty tracks: with space for five columns and three items, you get three filled columns and two empty ones holding their width, so the items stay at the left. auto-fit collapses the empty tracks to zero width, so the remaining fr values share the whole row and the three items stretch to fill it. Pick auto-fill when items must keep a consistent width whatever the count, and auto-fit when a half-empty row should still look full. Neither can be painted onto an area map, because the number of cells is not known until the container is measured, which is why they are not offered in the fields above.
What does minmax() do, and why does minmax(1fr, 300px) fail?
minmax() gives a track a floor and a ceiling: minmax(160px, 1fr) never goes below 160px, and above that shares leftover space like any other fr track. That combination is most of what responsive grid layout is made of. The reversed form fails because an fr value is a share of the space left over — it cannot be evaluated until the minimums of every track are known, so it cannot itself be a minimum. Put the fr in the second position and the declaration is valid; leave it in the first and the browser drops grid-template-columns entirely, taking your other tracks with it. The track fields above reject it with that explanation rather than passing it through.
Do I even need named areas? What about line numbers?
You do not need them, and for a grid of uniform tiles they are more ceremony than they are worth — `grid-column: span 2` on the one wide tile is shorter and clearer than naming every region. Named areas earn their place when a layout has distinct parts that a reader has to hold in their head: a header, a rail, a body and a footer are far easier to check in a quoted map than as eight line numbers spread across four rules. They also survive changes better, since inserting a column shifts every line number after it but leaves the names alone. One useful bonus: naming an area implicitly creates line names around it, so `grid-column: main-start / main-end` works without declaring anything further.
Where does the gap count when tracks are being sized?
Gaps are taken out first. The browser subtracts the total gap from the available space, sizes the fixed and content-based tracks, and only then divides what remains among the fr tracks. That is why adding a gap to a grid of fr columns narrows the columns rather than overflowing the container, and why the same grid built from percentages breaks instead. It also means a percentage gap is resolved against the container’s size on that axis, so a percentage row gap grows as the container gets taller.
What happens if I place more children than the grid has cells?
The grid creates implicit rows for them and keeps going. Explicit tracks are the ones you declared; anything past them lands in implicit tracks, which are sized by grid-auto-rows — `auto` by default, so they fit their content. That is often exactly what you want for a list of unknown length, and occasionally a trap: a child accidentally placed on row 9 of a three-row grid silently creates rows 4 through 8 as empty strips. This generator only draws the explicit grid, because implicit tracks depend on how many children you have at runtime, which is not something a static map can know.
Is Grid a replacement for Flexbox?
No, and the split is more useful than the rivalry. Grid places children into a structure you defined ahead of time, in two dimensions at once — you are describing the page, and the content goes where you put it. Flexbox distributes children along one axis according to their own sizes — you are describing a row or a column, and the content decides how much room it takes. A card layout is Grid; the row of buttons inside one of those cards is Flexbox. Most real pages use both, usually within a few elements of each other.