CSS Grid (formally the CSS Grid Layout Module) is a two-dimensional layout system in CSS that divides a container into rows and columns and places child elements into the resulting cells or areas. Unlike older techniques built on floats or inline blocks, it lets the author define the layout structure on the parent and then position items independently of their order in the HTML source.
In web development, CSS Grid is the standard tool for page-level layouts, card galleries, dashboards and any design in which content must align along both axes at once. It is supported by all current major browsers, and it complements Flexbox, which distributes items along a single axis.
How CSS Grid builds a layout
CSS Grid works by turning an element into a grid container with display: grid, after which its direct children become grid items. The author then defines tracks (rows and columns) with grid-template-columns and grid-template-rows, and the browser computes the size of every track before placing items onto the grid.
The space between two adjacent grid lines is a track; the intersection of one row track and one column track is a grid cell; any rectangle of cells is a grid area. Lines are numbered from 1 at the start edge and from -1 at the end edge, so grid-column: 1 / -1 makes an item span the full width regardless of how many columns exist.
The table below summarizes the core vocabulary that every grid declaration relies on.
| Concept | What it is | Typical property |
|---|---|---|
| Grid container | Element that establishes the grid formatting context | display: grid or inline-grid |
| Grid track | One row or one column of the grid | grid-template-columns, grid-template-rows |
| Grid line | Numbered or named boundary between tracks | grid-column-start, grid-row-end |
| Grid area | Rectangle spanning one or more cells | grid-template-areas, grid-area |
| Gap | Fixed gutter between tracks | gap, row-gap, column-gap |
| Implicit track | Track created automatically for overflow items | grid-auto-rows, grid-auto-flow |
When items outnumber the explicitly defined cells, the auto-placement algorithm creates implicit tracks sized by grid-auto-rows or grid-auto-columns. Setting grid-auto-flow: dense lets the algorithm backfill earlier holes, which produces a compact layout but can break the visual reading order.
Track sizing and the fr unit
Track sizing is where CSS Grid differs most from earlier layout methods, because it introduces the fr unit, which represents a fraction of the remaining free space after fixed and content-based tracks have been sized. A declaration such as grid-template-columns: 240px 1fr 2fr gives the first column a fixed width and splits the rest in a one-to-two ratio.
Several sizing functions combine with fr to create layouts that adapt without media queries. The most widely used values are compared below.
| Value | Behavior | Typical use |
|---|---|---|
fr | Shares remaining free space proportionally | Flexible main columns |
minmax(min, max) | Clamps a track between two sizes | Columns that must not shrink below a readable width |
auto | Sizes to content, then stretches if space remains | Header or footer rows |
min-content / max-content | Sizes to the smallest or largest content width | Labels and sidebars that fit their text |
fit-content(limit) | Grows with content up to a limit | Navigation columns with a cap |
repeat(auto-fit, ...) | Creates as many tracks as fit, collapsing empty ones | Responsive card grids |
The pattern repeat(auto-fill, minmax(16rem, 1fr)) is the canonical responsive grid without breakpoints: the browser fits as many 16rem columns as the container allows and stretches them evenly. The difference between auto-fill and auto-fit appears only when items do not fill a row: auto-fill keeps empty tracks, while auto-fit collapses them so existing items expand.
A frequent surprise is that 1fr has an implicit minimum of auto, so a long word or a wide image can force the track wider than its share. Writing minmax(0, 1fr) removes that minimum and prevents unexpected overflow.
CSS Grid compared with Flexbox and older methods
CSS Grid and Flexbox solve different problems: Grid is layout-first, defining the structure before content arrives, while Flexbox is content-first, letting item sizes drive distribution along one axis. Most production interfaces combine both, typically Grid for the page skeleton and Flexbox for components such as toolbars or button rows.
| Technique | Dimensions | Strength | Main limitation |
|---|---|---|---|
| CSS Grid | Two (rows and columns) | Precise alignment on both axes | More verbose for simple one-line rows |
| Flexbox | One (row or column) | Content-driven distribution and wrapping | Wrapped lines do not align with each other |
| Floats | None by design | Text wrapping around images | Requires clearing hacks for layout |
| Table layout | Two | Native alignment of cells | Ties presentation to tabular markup |
The subgrid value, defined in CSS Grid Layout Level 2 and now supported by all major engines, lets a nested grid inherit the parent’s tracks. It solves the classic problem of card components whose titles, images and buttons must line up across siblings. Grid also pairs well with container queries, which adapt a grid to the width of its component rather than the viewport.
Named areas and placement in practice
The grid-template-areas property lets an author draw the layout as text, assigning a name to each cell so that items are placed with a single grid-area declaration. This makes the structure readable at a glance and allows a complete layout change inside one media query by redefining only the area map.
Choosing the right placement method depends on how stable the structure is and who maintains it, as the decision table shows.
| Situation | Recommended method | Reason |
|---|---|---|
| Fixed page skeleton | Named template areas | Self-documenting and easy to rearrange |
| Unknown number of cards | Auto-placement with repeat() | No manual positions to maintain |
| Overlapping hero image and text | Line-based placement | Items can share cells and layer with z-index |
| Aligned content inside nested cards | Subgrid | Children align to the parent tracks |
| Single row of controls | Flexbox instead of Grid | One-dimensional distribution is simpler |
Alignment uses the Box Alignment properties shared with Flexbox: justify-items and align-items position items inside their cells, while justify-content and align-content position the entire grid inside the container when the tracks do not fill it.
Common mistakes and accessibility risks
The most serious risk in CSS Grid is a mismatch between visual order and source order. Screen readers and keyboard focus follow the HTML sequence, so moving items with order, explicit placement or dense packing can make tab navigation jump unpredictably across the screen.
| Symptom | Cause | Fix |
|---|---|---|
| Column overflows its share | Implicit auto minimum of 1fr | Use minmax(0, 1fr) |
| Focus order jumps around | Visual placement differs from source order | Reorder the HTML, not only the CSS |
| Unexpected extra rows | Items fall into implicit tracks | Define grid-auto-rows or fix spans |
| Nested items misaligned | Inner grid ignores parent tracks | Use grid-template-columns: subgrid |
| Margins collapse expectations | Grid items do not collapse margins | Use gap for gutters |
Another common error is rebuilding a grid system with utility classes that recreate a twelve-column framework. Native CSS Grid makes most of that markup unnecessary, and fewer wrapper elements simplify both styling and the accessibility tree.
A plain-language summary
CSS Grid is a way of telling a web browser to lay out a page like a sheet of graph paper. The designer decides how many columns and rows there are and how wide each one should be, and then drops each piece of content into its place.
Before it existed, building a neat page with a header, a sidebar and a main area required workarounds. Now the layout is described in a few lines, and the browser does the arithmetic. The key point is simple: Grid handles rows and columns together, which is why it suits whole pages and galleries.
An example from practice
A front-end developer at an online retailer rebuilds the product listing page, which previously used a float-based framework with separate markup for phones, tablets and desktops. The new container uses grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)) with a gap of 1.5rem, so the page shows two columns on a phone and five on a wide monitor without a single breakpoint.
Each product card is itself a grid with grid-template-rows: subgrid, so images, titles, prices and buttons align across the whole row even when product names wrap to different lengths. The page skeleton around the listing uses named areas for the header, filters and results, and one media query swaps the filters from a sidebar to a top bar.
The result is roughly a third less layout CSS, the removal of several wrapper elements, and a keyboard order that still follows the HTML, because the team kept the source order identical to the visual order.