A CSS Grid generator: draw areas by dragging cells
The box on the stage of this CSS Grid generator is a real display: grid container. The rulers above and to the left show each column and row track with the value you wrote, the px size the browser computed, and the line numbers. Drag across empty cells to create an area that covers that range, drag an area to move it cell by cell, and drag the bottom-right corner of the selected area to extend or shrink it. Click a chip on a ruler to change that track to something like 1fr, 200px or minmax(120px, 1fr).
The sentence under the stage walks through how big 1fr is right now, in calculation order, and for auto-generated columns it shows how many columns fit as a formula. Copy the finished layout as CSS that uses grid-template-areas with area names or with line numbers, or take the HTML along with it. Copy link stores the current layout in the address.
When Grid fits: aligning rows and columns together
CSS Grid is a two-dimensional layout: you define the column and row boundaries first, then place items into those cells. Because the tracks, not the item content, decide the cell sizes, the edges line up exactly from one row to the next. It is especially useful in screens like these.
- Page skeleton: split fixed regions such as header, sidebar, main content and footer. With area names, the CSS alone shows the layout (examples "Basic page" and "Holy Grail").
- Dashboard: place several small metric cells and a large chart that spans two or more cells in the same grid (example "Dashboard").
- Card lists: keep every card the same width on every row even as the column count changes with the screen width. When you wrap a row in Flexbox, the cards on the last row tend to stretch; Grid cards follow the track width, so they don't (example "Auto-fit cards").
- Photo collections with mixed sizes: mix photos that span two or four cells and fill the holes with
dense(example "Photo mosaic"). - Layering: give two areas the same cells and you can put text over an image without any position math. Drag an area over another one on the stage to see the overlap.
For a menu or a button group that is laid out in one direction and shares space only within that line, Flexbox is simpler.
How to use it
- Pick an example. Click one of the 9 tiles at the top and the tracks, gaps and areas all change at once. Areas that change slide from their old position to the new one.
- Set the tracks. Add or remove tracks with the column and row buttons in the top row, and click a ruler chip to change a size. In the panel on the right you can type a value or choose a common one, and insert a track before or after the selected one.
- Draw the areas. Drag across empty cells to create an area, drag an area to move it, and drag its bottom-right corner to resize it. With the keyboard, focus an area and use the arrow keys to move it one cell, Shift with the arrow keys to extend or shrink it, and Delete to remove it. Rename an area in the panel.
- Check the widths. Change the container width with the Full, 768, 480 and 360 buttons in the top row or the handle at the bottom right of the stage, and you immediately see how the fr tracks shrink and how many auto columns remain.
- Copy. Choose area names or line numbers, then click Copy CSS or Copy HTML + CSS. Turn on Include preview styling to add borders and backgrounds, so the cells are visible even when you paste into an empty file.
How to read track values
grid-template-columns and grid-template-rows take one size per track. When the same value repeats you can shorten it to something like repeat(3, 1fr), and this tool writes it that way when all tracks are equal.
| Value | How the size is decided | Use it when |
|---|---|---|
200px | Fixed, regardless of the container width | Sidebars, icon cells |
25% | A share of the container's inner width; gaps are added on top | You need an exact proportion (tracks totaling 100% plus gaps overflow) |
1fr | A share of the space left after other tracks and gaps | You want to fill all the remaining space |
auto | Content size; stretches into the leftover space when there is no fr track | Header and footer rows whose height should follow the content |
min-content | The narrowest width the content can take (the longest word) | A cell that should stay as narrow as possible, like a label |
max-content | The width of the content on one line, without wrapping | Short items that must not wrap |
minmax(120px, 1fr) | From a minimum of 120px up to a maximum of 1fr | A cell that must not get too narrow |
minmax(0, 1fr) | From a minimum of 0 up to a maximum of 1fr | Splitting column widths evenly even when a long word is present |
fit-content(240px) | Follows the content size but never exceeds 240px | Narrow when the content is short, up to the cap when it is long |
How many px is 1fr: the order for sharing leftover space
An fr is not a fixed length but a factor for dividing the leftover space. The browser decides the size of 1fr in this order.
- Subtract the gaps (number of tracks − 1) and the sizes of the non-fr tracks from the inner width. What remains is the leftover space.
- Divide the leftover space by the sum of the fr factors to get the size of 1fr. If the factors add up to less than 1, you divide by 1 instead, so part of the leftover space stays empty.
- Any fr track whose minimum size is larger than its share is fixed at that minimum, and the remaining fr tracks share what is left again. The minimum size of
1fris auto, so the width of a long word or an image in the cell becomes the minimum.
The table below shows the column widths that Chromium 151 computes for a container with an inner width of 800px and a column gap of 16px (only the last row uses an inner width of 600px and no gap). Enter the same values on the stage and the sentence below it shows the same formula.
| grid-template-columns | Leftover space | 1fr | Column widths |
|---|---|---|---|
1fr 1fr 1fr | 768px | 256px | 256 · 256 · 256 |
200px 1fr | 584px | 584px | 200 · 584 |
1fr 2fr 1fr | 768px | 192px | 192 · 384 · 192 |
240px 1fr 2fr | 528px | 176px | 240 · 176 · 352 |
25% 1fr | 584px | 584px | 200 · 584 |
0.5fr 0.25fr | 784px | 784px (factors total 0.75 → divided by 1) | 392 · 196 (196px left empty) |
minmax(240px, 1fr) 1fr 1fr | 600px → 360px | 180px | 240 · 180 · 180 |
In the last row, the browser first divides 600 ÷ 3 = 200px, but the first column's minimum of 240px is larger than the 200px share, so that column is fixed at 240px. The remaining 360px is split between the other two columns at 180px each. A 2fr column is exactly twice a 1fr column only when both shares are larger than their minimum sizes.
Area names and line numbers
A grid with n columns has vertical lines numbered 1 through n + 1. The small numbers on the stage rulers are these line numbers. grid-column: 1 / 3 runs from line 1 to line 3, so it covers two columns, and grid-row: 2 / 4 covers two rows. The last line can also be written as -1, so 1 / -1 is the full width whatever the column count.
grid-template-areas takes one pair of quotes per row and one area name per cell. An empty cell is a period (.). Written with area names, the "Basic page" example looks like this.
.grid {
display: grid;
grid-template-columns: 180px 1fr;
grid-template-rows: 56px 1fr 48px;
grid-template-areas:
"header header"
"side main"
"footer footer";
gap: 12px;
}
.header { grid-area: header; }
.side { grid-area: side; }
The cells that share a name must form a single rectangle. If they are L-shaped or separated, the whole declaration is invalid. The items given grid-area: name then can't find their area, and they all stack in the same cell created outside the explicit grid. Every string must also have the same number of cells. A layout where two areas share the same cells can't be written with names, so when an overlap appears this tool switches to line numbers and tells you why above the code.
Area names let you picture the layout from the CSS alone, and you can redo the layout per screen width by changing only the strings in a media query. Line numbers are shorter when there are many cells, as in a 12-column grid, or when areas must overlap.
auto-fit and auto-fill: letting the width decide the column count
Choose "Auto by width" and the column definition takes the form repeat(auto-fill, minmax(min(100%, 160px), 1fr)). The browser creates as many columns as fit without overflowing, and the count is ⌊(inner width + gap) ÷ (min width + gap)⌋. min(100%, 160px) lowers the minimum width to the parent's width when the parent is narrower than 160px, so a single column doesn't spill out sideways.
auto-fit and auto-fill look different only when there are fewer areas than columns. auto-fill keeps the empty columns as space, while auto-fit collapses empty columns to 0 along with the gaps on either side, so the remaining areas grow wider. With a 160px minimum, a 16px gap and 3 areas, the result is as follows.
| Inner width | Columns | auto-fill cell width | auto-fit cell width |
|---|---|---|---|
| 360px | 2 | 172px | 172px |
| 600px | 3 | 189.3px | 189.3px |
| 800px | 4 | 188px | 256px |
| 1000px | 5 | 187.2px | 322.7px |
At 360px there are 2 columns, so the third area drops to the next row, and at 600px the area count equals the column count, so both methods match. Choose auto-fill if you don't want a card or two to grow too wide, and auto-fit if you want the cards you have to fill the width.
Beyond the explicit grid: implicit rows and dense
When there are more areas than the rows set with grid-template-rows, the browser adds more rows. These are called implicit rows, and grid-auto-rows sets their size (default auto). On the stage, the chips and cells of implicit tracks are drawn with dotted lines. Card lists often define no rows at all and set only grid-auto-rows.
An area with no position looks for a free cell in HTML order. If a two-cell area doesn't fit at the end of a row, it moves to the next row and leaves a hole before it. With grid-auto-flow: dense, a later, smaller area fills the earlier hole. In the "Photo mosaic" example, turning Fill gaps off leaves row 1, column 4 empty, and turning it on moves p5 up into that spot. Note that dense changes only the visual order; Tab key order and screen reader order stay in HTML order.
Alignment inside a cell
By default an area fills its cell with stretch. justify-items sets the horizontal position inside the cell and align-items the vertical position; with start, center or end, the area shrinks to its content size and sits there. Setting both to center is the same as the single line place-items: center, and the "Centered" example shows that layout. To align just one area, give it justify-self and align-self.
When the tracks add up to less than the container, the whole set of tracks sticks to the start side (top left). This is the case the sentence under the stage describes as "the right side is empty"; to move the set to the middle, add justify-content: center to the container.
Browser support
Below are the first supported versions listed in MDN's browser compatibility data. The code this tool generates does not use subgrid.
| Feature | Chrome | Firefox | Safari |
|---|---|---|---|
display: grid, grid-template-columns | 57 | 52 | 10.1 |
grid-template-areas | 57 | 52 | 10.1 |
gap (grid) | 66 | 61 | 12 |
min() function | 79 | 75 | 11.1 |
| subgrid (for reference) | 117 | 71 | 16 |
Common mistakes
- The 1fr columns have different widths. A cell holds a long URL or a large image whose minimum size is larger than its share. Switch to
minmax(0, 1fr)and wrap the overflowing text separately. - Four 25% tracks overflow. Gaps are not included in % tracks. With a 16px gap, 48px overflows, so use
repeat(4, 1fr). - A 1fr row doesn't grow. If the container height is auto, there is no leftover height to share. Give it a height, such as
heightormin-height: 100dvh. - The whole grid-template-areas is ignored. Either the strings have different numbers of cells, or a name doesn't form a rectangle.
- Cards overflow sideways on narrow screens.
minmax(240px, 1fr)holds 240px even when the parent is narrower. Change it tominmax(min(100%, 240px), 1fr). - An extra column appears and causes horizontal scrolling. A span larger than the column count creates an implicit column. It happens most often in auto columns at narrow widths, so check with the 360px button.
- The reading order is off after filling with dense. Only the visual order changed. For a list where order matters, turn dense off or fix the HTML order.
Limits and cautions
- The stage shows only the left-to-right horizontal writing direction.
- The areas on the stage contain only their name, and they have
min-width: 0andoverflow: hiddenso that the text doesn't widen the tracks. The default code holds layout properties only and leaves these two lines out, so even the same name can widen a 1fr column, whose minimum is auto, to the width of the text. Turn on 'Include preview styling' to add these two lines with the stage's padding, border and font size, and the code draws at the same size as the stage. With real content, auto, min-content and fr tracks will size differently from the stage. - You can create up to 12 explicit tracks each for columns and rows, and up to 24 areas. Named lines, negative line numbers, subgrid,
grid-auto-columns,justify-contentandalign-content, per-areajustify-selfandalign-self, and media queries are not put in the code. - The value in the Height field is the content height without padding. The
heightin the code is written with the same meaning (content-box). - The 768, 480 and 360 buttons change the container width. This differs from a media query, which is based on the screen (viewport) width.
- The fr explanation recalculates from the track sizes the browser drew. If they differ by more than 0.6px, it tells you the formula is approximate.
- Copy link stores the area names in the address too.
As of The W3C CSS Grid Layout Module Level 2 (Candidate Recommendation Draft 2025-03-26) and the MDN browser compatibility data were checked against the originals on 2026-10-21. The column widths in the tables were measured on a real grid container in Chromium 151.
Sources
- 01W3C: CSS Grid Layout Module Level 2Candidate Recommendation Draft 2025-03-26. §6.6 automatic minimum size of grid items, §7.2.1 track sizes (fr outside minmax() has a minimum of auto), §7.2.3.2 auto-fill and auto-fit repetition count and collapsing empty tracks, §7.3 grid-template-areas (the declaration is invalid unless the areas are rectangles), §7.5 the implicit grid and grid-auto-rows, §7.7 dense in grid-auto-flow, §11.1 gutters (gaps of collapsed tracks), §12.7.1 finding the size of an fr (factors summing to less than 1, recalculating after excluding tracks with a larger minimum)
- 02MDN: grid-template-columnsTrack value syntax and browser compatibility table: Chrome 57, Firefox 52, Safari 10.1; subgrid Chrome 117, Firefox 71, Safari 16
- 03MDN: grid-template-areasArea name syntax and browser compatibility table: Chrome 57, Firefox 52, Safari 10.1
- 04MDN: gapBrowser compatibility table: in grid layout, Chrome 66, Firefox 61, Safari 12
- 05MDN: min()Browser compatibility table: Chrome 79, Firefox 75, Safari 11.1
Frequently asked questions
How do I make 3 equal-width columns with CSS Grid?
Give the container display: grid and grid-template-columns: repeat(3, 1fr). With an inner width of 800px and a column gap of 16px, the 768px left after the 32px of gaps is split three ways, so each column is 256px. To keep the widths identical even when a cell holds a long URL or an image, write repeat(3, minmax(0, 1fr)).
What is the difference between fr and %?
A % is a share of the container's inner width, so gaps are added on top, while fr divides the space left after other tracks and gaps are subtracted. With a 16px gap, four 25% tracks overflow by 48px, but four 1fr tracks do not.
What is the difference between auto-fit and auto-fill?
Both create as many columns as fit without overflowing, but when there are fewer areas than columns, auto-fill keeps the empty columns as space and auto-fit collapses them to 0 so the remaining areas grow wider. With an inner width of 800px, a 160px minimum, a 16px gap and 3 areas, auto-fill gives 188px and auto-fit gives 256px.
I wrote grid-template-areas but it isn't applied. Why?
Check that every string has the same number of cells and that the cells sharing a name form a single rectangle. If either rule is broken, the whole declaration is invalid. Each item also needs its own grid-area: name, and the names are written without quotes.
How do I make the column count change with the screen width?
Write something like grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr)) and the column count follows the width without a media query. The count is (inner width + gap) ÷ (min width + gap), rounded down. In this tool, choose "Auto by width" and change the width with the handle to see where the columns change.
How do I center an item inside a Grid?
Give the container place-items: center, or set both justify-items and align-items to center. To center vertically, the cell needs spare height, so set the height of the row or the container as well. Click the "Centered" example to see it at once.
Why doesn't my 1fr row grow?
If the container height is auto, there is no leftover height to share, so the fr row grows only to its content height. Set a container height, such as height or min-height: 100dvh, and the 1fr row takes the leftover height.
Should I use Grid or Flexbox?
Grid suits page skeletons, dashboards and card lists, where row and column edges must line up together; Flexbox suits menus and button groups that line up in one direction and share space within that line. The two are often combined: use Grid to divide the regions and Flexbox to align the row of buttons inside each region.