Build a CSS Grid layout and instantly generate CSS grid code you can copy into your project.
display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 80px); column-gap: 12px; row-gap: 12px;
repeat(auto-fit, minmax(...))), 12-column system, Photo Gallery with asymmetric spans, Dashboard, or Magazine. Or start with the empty grid and build from scratch.1fr (flexible, divides leftover space), auto (sized by content), fixed px, percentage, or minmax(min, max) for responsive bounds. Common patterns: repeat(3, 1fr) for three equal columns, 200px 1fr 200px for fixed sidebars with a flex middle, or repeat(auto-fit, minmax(240px, 1fr)) for a responsive card grid.16px / 24px) — the gap shorthand replaces margin hacks. Pick justify-items (main-axis alignment of items in their cells: stretch, start, center, end) and align-items (cross-axis). Click any cell to place an item and drag its handles to span multiple tracks via grid-column: span 2 / grid-row: span 3..grid { display: grid; grid-template-columns: ...; ... } rules straight into your stylesheet — nothing uploads, everything renders in your browser session.CSS Grid Layout (Level 1) is the two-dimensional layout system every modern web framework relies on for page-level structure — headers, sidebars, content areas, footers, dashboards, and card walls. Per caniuse, Grid Level 1 has ~95.6% global browser support and shipped in every major engine during 2017 (Chrome 57, Edge 16, Firefox 52, Safari 10.1, Opera 44). The property surface is large though — grid-template-columns, grid-template-rows, grid-template-areas, grid-auto-flow, repeat(), minmax(), auto-fit vs auto-fill, named lines, implicit tracks, place-content shorthands — and getting them right by trial-and-error in DevTools is slow. Building visually shortens the loop.
auto 1fr auto) and a grid-template-areas block names the header, sidebar, main, and footer regions. Children get grid-area: header and the browser places them. No floats, no negative margins, no JS.grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)) automatically reflows from 4 columns on desktop down to 1 column on mobile based on container width alone.grid-column: 1 / span 8, pull-quote at grid-column: 9 / -1) that previously meant absolute positioning or float-based hacks.grid-template-columns: max-content 1fr lines every label up to the widest one and stretches inputs to fill the rest. No more display: table workarounds.grid-row: span 2 on tall portraits) combined with grid-auto-flow: dense packs items into earlier gaps instead of leaving them empty.After your stylesheet is ready, tidy it during development with the CSS Formatter and ship the production build through the CSS Minifier.
grid-template-columnsThe single biggest source of Grid confusion is which unit to put in each track slot. They're not interchangeable.
| Unit | Meaning | Typical use |
|---|---|---|
1fr |
One fraction of the leftover space after fixed/content tracks are subtracted | Flexible columns that should share remaining width |
px / rem |
Fixed length; never grows or shrinks | Sidebars, icon columns, fixed-width gutters |
% |
Percentage of the grid container's inline size | Rarely needed — fr is usually better |
auto |
Sized to the largest item in that track (content-based) | Label columns, "shrink-wrap to content" |
min-content |
Smallest the content can shrink to without overflow (often the longest word) | Compact text columns where wrapping is fine |
max-content |
Width content would take on a single line | "Stretch as wide as the unbroken text" |
minmax(a, b) |
A range — at least a, at most b |
Responsive bounds, e.g. minmax(200px, 1fr) |
fit-content(N) |
Like auto but capped at N |
"As wide as content needs, but never more than 300px" |
repeat(N, ...) |
Shorthand for N identical tracks | repeat(12, 1fr) for a 12-column framework grid |
repeat(auto-fit, ...) |
Browser fits as many tracks as it can, then collapses empties | Responsive card grids that stretch |
repeat(auto-fill, ...) |
Same as auto-fit but preserves empty tracks | Card grids that should stay aligned to a phantom column count |
fr math: the browser subtracts every non-fr track size and every gap from the container width, then divides the remainder by the sum of fr values. So 200px 1fr 2fr in a 1000px container with 0 gap leaves 800px → 1fr is ~267px and 2fr is ~533px.
| Property | CSS Grid | Flexbox |
|---|---|---|
| Axis | Two-dimensional — rows AND columns at once | One-dimensional — single row OR single column |
| Best for | Page-level macro layout, dashboards, card walls | Component-level micro layout — nav bars, button rows, form fields |
| Item placement | Explicit by grid line / area name | Implicit, source-order based with order overrides |
| Sizing units | fr, minmax(), repeat(auto-fit, ...) |
flex-grow / flex-shrink / flex-basis |
| Alignment across rows | Yes — items in different rows line up to shared columns | No — each line is independent |
| Browser support (caniuse) | ~95.6% global | ~96% global |
The two layout systems compose. Use Grid for the page macro-layout (where rows and columns must align across both axes) and Flexbox inside each grid cell for micro-layout like centering a button row.
If your layout has items that must align across BOTH rows and columns at once — a card grid, a dashboard, a page with header/sidebar/main/footer — start with Grid. If you're laying out a one-dimensional row (nav links, button toolbar, form field row) or aligning items inside one container, start with Flexbox. The two compose well: Grid for the outer skeleton, Flexbox for components inside each cell. A common pattern is display: grid; grid-template-columns: 240px 1fr for a page shell, then display: flex; justify-content: space-between inside the header for the logo + nav.
auto-fit and auto-fill in repeat()?Both tell the browser "fit as many tracks as you can with this minmax range". The difference shows up only when you have fewer items than the container could fit columns. auto-fill preserves the extra columns as empty tracks, so the existing items stay their minimum size and you get visible gaps on the right. auto-fit collapses those empty tracks to zero width and lets the existing items stretch to absorb the space. For a card grid where items should fill the row no matter how few you have, use auto-fit. For a grid where items should stay a consistent size and never stretch (so a 4th item dropped in later lines up neatly), use auto-fill. The reference test case is repeat(auto-fit, minmax(240px, 1fr)) vs repeat(auto-fill, minmax(240px, 1fr)) on a 1200px container with only 2 cards — auto-fit gives two ~600px cards, auto-fill gives two 240px cards with empty space on the right.
grid-template-areas instead of line numbers?Use named areas when the layout has a stable semantic structure — header, nav, main, aside, footer — that you want to read at a glance in CSS. The ASCII-art template ("header header header" "nav main aside" "footer footer footer") doubles as documentation and lets you reshape the layout for mobile by redefining the template inside a media query without touching child CSS. Each child element gets grid-area: header (or whichever) and the browser places it. Use line numbers (grid-column: 1 / span 3) for one-off placements inside an otherwise uniform grid (a hero card spanning multiple columns) where giving everything a name would be noise.
Yes, in every evergreen browser. Firefox shipped subgrid first in version 71 on December 3, 2019. Safari followed in version 16.0 (September 2022). Chrome and Edge both shipped it in version 117 on September 12, 2023. Per caniuse, global support is ~89.8%. Subgrid lets a nested grid inherit its parent's track sizes — useful when child cards need their own internal layout to align with the outer grid's column lines (e.g., a card title row that lines up across every card in a row, even when the cards themselves have different content lengths). If you need to support browsers older than ~2.5 years on Chromium, ship a non-subgrid fallback.
fr actually compute?The browser does a two-pass layout. Pass 1: subtract every non-fr track size (px, %, auto, min-content, max-content, content-sized minmax tracks at their min) and every gap from the container's inline size. Pass 2: divide the remainder by the sum of fr values, multiply each fr track by its share. So grid-template-columns: 200px 1fr 2fr with gap: 20px in a 1000px container: subtract 200 (fixed) + 40 (two gaps) = 240 leftover for fr = 760. Sum of fr = 3, so 1fr ≈ 253px and 2fr ≈ 507px. minmax(min, 1fr) complicates this — if a track's content forces it past its min, that "stolen" space comes out of the fr pool.
grid-template-columns and grid-template-rows define the explicit grid — the tracks you've named. When items overflow that grid (you placed a child at grid-row: 5 but only defined 3 rows), the browser creates implicit tracks to hold them. By default implicit tracks are sized auto. Use grid-auto-rows / grid-auto-columns to control their sizing — grid-auto-rows: minmax(100px, auto) is common to give each implicit row a minimum height while still letting tall content expand it. grid-auto-flow: row dense tells the browser to fill earlier gaps with later items, useful for packed photo galleries.
place-content, place-items, and place-self do?They're shorthands that combine the align-* and justify-* properties into one declaration. place-content: center is the same as align-content: center; justify-content: center — it aligns the whole grid track block inside the container (only meaningful when the grid is smaller than its container). place-items: center start sets align-items: center; justify-items: start — aligns every grid item inside its cell. place-self is the child-level override (set on individual items). Order is align-* justify-* (block axis first, then inline axis) — easy to flip by accident.
grid-row: span 2 on some of them?You've got more spans than your explicit grid has rows, so the browser is auto-placing the spanning items wherever they fit — sometimes on top of explicitly-placed items if the row numbers collide. Three fixes: (1) add enough explicit rows via grid-template-rows: repeat(N, ...), (2) set grid-auto-flow: dense to let the browser pack items into earlier gaps instead of overlapping, or (3) place each spanning item explicitly with grid-row: 1 / span 2 so its start line is locked.
Email clients are the holdout — Gmail, Outlook, and Apple Mail all still expect table-based layouts; CSS Grid renders inconsistently and many clients strip it. For email, use tables. For PDF generators that render via Chromium (Puppeteer, Playwright, headless Chrome) Grid works fine — it's the same engine as the browser. For PDF generators using older engines (wkhtmltopdf based on WebKit from 2014) Grid support is incomplete; test before shipping.