CSS Grid Cheatsheet - Tracks, Areas & Auto-Fit

There is only one line between Grid and Flexbox, but crossing it is expensive: control a single axis and use Flex; align rows and columns together and use Grid. Page skeletons, a fixed header plus sidebar and content, or a dashboard of panels, are inherently two-dimensional. Forcing those onto nested Flex containers means three levels of wrappers just to line up columns, and the stylesheet collapses into indirection quickly. Size tracks with fr and minmax rather than percentages: fr shares leftover space in proportion, and minmax(200px, 1fr) gives each column a floor while keeping it flexible, so auto-fit and auto-fill produce responsive behaviour with no media queries at all. Sections cover the container, spacing and alignment, placing and spanning items, explicit and implicit tracks, common patterns, and responsive techniques, including grid-template-areas, which trades a little flexibility for a layout you can read at a glance. Naming tracks and areas with grid-template-areas costs some flexibility and buys a layout a colleague can read without tracing selectors.

Web Services·42 commands·Last updated 2026-07-21
cssgridLayoutFrontend

Typical Use Case

For engineers laying out whole pages rather than one component: dashboards, card walls, galleries and form grids. Grid lets the parent define tracks while children declare where they land, instead of nested floats or piles of calc. Four groups: container definitions with repeat and named areas, spacing and alignment through gap and the justify and align families, spanning cells with line numbers and span, and implicit tracks. The traps are auto-fill versus auto-fit and 1fr inside minmax.

Container Definition 8

display: grid
Enable grid layout (block-level)
display: inline-grid
Enable grid layout (inline-level)
grid-template-columns: 1fr 2fr 1fr
Define column tracks & ratio
grid-template-rows: 100px auto 100px
Define row tracks & sizes
grid-template-columns: repeat(3, 1fr)
Repeat 3 equal columns
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr))
Auto-fill responsive columns
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))
Auto-fit responsive columns
grid-template-areas: "header header" "sidebar main" "footer footer"
Visual named-area layout

Gap & Alignment 10

gap: 16px
Row & column gap
row-gap: 16px
Row gap only
column-gap: 16px
Column gap only
justify-items: stretch
Stretch horizontally in cell (default)
justify-items: center
Center horizontally in cell
align-items: center
Center vertically in cell
place-items: center
Set both horizontal & vertical
justify-content: center
Align grid horizontally
align-content: center
Align grid vertically
place-content: center
Set both grid alignments

Cells & Spans 7

grid-area: header
Place item into named area
grid-area: 1 / 1 / 3 / 3
Line-based: row-start/col-start/row-end/col-end
grid-column: 1 / 3
Span columns: line 1 to 3
grid-row: 1 / span 2
Span 2 rows from line 1
grid-column: 1 / -1
Span full row (line 1 to end)
grid-column: span 2
Span 2 columns
grid-row: span 3
Span 3 rows

Explicit & Implicit Tracks 6

grid-auto-columns: 100px
Implicit column track size
grid-auto-rows: 100px
Implicit row track size
grid-auto-flow: row
Auto-placement: by row (default)
grid-auto-flow: column
Auto-placement: by column
grid-auto-flow: dense
Dense packing, fill gaps
grid-auto-flow: row dense
Row-wise dense packing

Common Patterns 6

grid-template-columns: 1fr 1fr 1fr
Three equal columns
grid-template-columns: 200px 1fr
Sidebar + main content
grid-template-columns: repeat(12, 1fr)
12-column grid system
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr)
Prevent overflow break
grid-template-areas: "header" "main" "footer"
Single-column stacked layout
grid-column: 1 / -1
Item spans full row

Responsive Tips 5

repeat(auto-fit, minmax(200px, 1fr))
Auto-fit, empty tracks collapse
repeat(auto-fill, minmax(150px, 1fr))
Auto-fill, keeps empty tracks
repeat(auto-fit, minmax(min(100%, 250px), 1fr))
min() prevents small-screen overflow
@media (max-width: 768px) { grid-template-columns: 1fr }
Single column on mobile
grid-auto-rows: minmax(150px, auto)
Responsive row height

Parameter matrix

ParameterEffectExample
display: gridMakes the container a grid formatting context; direct children become grid items automatically.display: grid
display: inline-gridThe grid participates in the outer flow as an inline-block, shrinking to its content — good for small controls embedded in text.display: inline-grid
grid-template-columns: 1fr 2fr 1frExplicit column tracks; fr distributes the leftover space. The most common three-column line.grid-template-columns: 1fr 2fr 1fr
grid-template-rows: 100px auto 100pxExplicit row tracks; auto rows are sized by content. The classic blend of fixed and flexible rows.grid-template-rows: 100px auto 100px
grid-template-columns: repeat(3, 1fr)Repeats a track definition N times, saving a long hand-written string of 1fr.grid-template-columns: repeat(3, 1fr)
repeat(auto-fill, minmax(200px, 1fr))Derives the column count from container width: empty tracks are still filled, so on a narrower container the last row aligns to the left.grid-template-columns: repeat(auto-fill, minmax(200px, 1fr))
repeat(auto-fit, minmax(200px, 1fr))Same as above but collapses empty tracks, so items stretch to fill when the container widens — the go-to for responsive card walls.grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))
grid-template-areasNames regions with a string matrix, paired with grid-area for placement; the classic header/sidebar/main three-part layout.grid-template-areas: "header header" "sidebar main" "footer footer"
gap / row-gap / column-gapTrack spacing, replacing the margin hack, and it does not add outer margins at the container edges.gap: 16px
justify-items / align-itemsControls how grid items align within their track: justify along the inline axis, align along the block axis.align-items: center
justify-content / align-contentControls how the whole track set is distributed inside the container; only has effect when items are fewer than tracks.justify-content: center
place-items / place-contentShorthand for align and justify, setting both axes at once.place-items: center
grid-area: 1 / 1 / 3 / 3Declares row and column spanning at once with start/end lines; the order is row-start / column-start / row-end / column-end.grid-area: 1 / 1 / 3 / 3
grid-column: 1 / 3Explicit column spanning; writing 1 / -1 spans from the first column to the last — the common full-bleed pattern.grid-column: 1 / -1
grid-row: 1 / span 2Starts at row 1 and spans 2 rows; the span form does not depend on the total row count, so it holds up better across redesigns.grid-row: 1 / span 2
grid-column: span 2Declares only the span with no start, letting the auto-placement algorithm decide where it lands — key to adaptive tile layouts.grid-column: span 2
grid-auto-rows: 100pxSets the height of implicit rows; without it they are content-driven and card heights tend to vary.grid-auto-rows: 100px
grid-auto-columns: 100pxSets the width of implicit columns, common in horizontally scrolling containers.grid-auto-columns: 100px
grid-auto-flow: row / columnDecides the auto-placement direction: row fills row by row, column fills column by column.grid-auto-flow: column
grid-auto-flow: denseBackfills gaps: small items slot into earlier gaps, at the cost of visual order detaching from DOM order.grid-auto-flow: dense
grid-template-columns: 200px 1frFixed sidebar plus flexible main content, the most common line for admin pages.grid-template-columns: 200px 1fr
grid-template-columns: repeat(12, 1fr)A 12-column grid; use grid-column: span N for proportional splitting, more reliable than hand-written percentages.grid-template-columns: repeat(12, 1fr)
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr)Pins the fr minimum to 0, equivalent to allowing shrink, so long content does not burst the track.grid-template-columns: minmax(0, 1fr) minmax(0, 1fr)
repeat(auto-fit, minmax(min(100%, 250px), 1fr))min(100%, 250px) keeps a single column on narrow screens from being pushed into horizontal scroll by the 250px minimum.grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr))
@media (max-width: 768px) { grid-template-columns: 1fr }Collapses multiple columns into one at the breakpoint — the easiest landing spot for responsive Grid.@media (max-width: 768px) { grid-template-columns: 1fr }
grid-auto-rows: minmax(150px, auto)Gives implicit rows a minimum height while allowing content to grow; covers both needs for a tile wall.grid-auto-rows: minmax(150px, auto)

Common pitfalls

Symptomauto-fill and auto-fit look the same, but the last row of items does not fill the full row.

Causeauto-fill keeps empty tracks (1fr still gets space), while only auto-fit collapses empty tracks and gives the space to existing ones.

FixUse auto-fit when you want full-width fill; use auto-fill when you need alignment preserved (background stripes, numbered columns).

SymptomA horizontal scrollbar appears on narrow screens and content is cut off halfway.

CauseThe 200px in minmax(200px, 1fr) is a hard lower bound, so tracks stay 200px even when the viewport is narrower.

FixWrite the lower bound as min(100%, 200px), or set grid-template-columns to 1fr at the breakpoint.

SymptomOne column is stretched very wide by a long URL or code block and the others are squeezed.

CauseThe lower bound of fr is actually auto, i.e. the content minimum size; long words or nowrap push the track open.

FixAdd overflow-wrap: anywhere to the content, or write the track as minmax(0, 1fr) to allow shrinking at the root.

SymptomA child sets grid-column: 1 / 3 but does not span where expected — the element lands on the next row.

CauseThe four values of grid-area and grid-column/grid-row are row-start / column-start / row-end / column-end, the opposite of the intuitive order.

FixVerify row/column order value by value, or declare grid-column and grid-row separately, which is harder to get wrong.

SymptomItems have uneven heights and card bottoms do not line up.

CauseImplicit row heights are content-driven and inconsistent; only align-items was set and the row tracks were not handled.

FixGive implicit rows a common lower bound with grid-auto-rows: minmax(150px, auto), or switch to an explicit row template.

SymptomAfter adding dense, tab order / reading order no longer matches visual order.

Causedense backfills later items into earlier gaps, detaching DOM order from visual position.

FixDo not use dense when focus order and screen-reader order matter; instead use explicit grid-column to place each tile.

SymptomA grid-template-areas layout suddenly collapses into a single column somewhere.

CauseSpaces and newlines in the areas strings must align strictly to rows and columns; a row with fewer tokens than the others makes that row invalid.

FixKeep the token count per areas row equal and each named region rectangular; use minmax(0, 1fr) as a fallback when needed.

SymptomWith nested grids the inner gap stacks on the outer one, doubling the actual spacing.

Causegap is not an inheritable margin; when both parent and child set gap, each takes effect.

FixDecide clearly which layer owns the spacing (usually the outer one) and let the inner one set only padding.

Troubleshooting

  1. 1Confirm the container really is a grid formatting context.

    grep -n "display: grid" -r src

    Putting display on a child item instead of the container has no effect.

  2. 2Cross-check the areas definition against the grid-area names.

    grep -n "grid-template-areas\|grid-area" -r src

    When names are misspelled, the region is treated as an ordinary unnamed grid item.

  3. 3Find the element introducing horizontal scroll.

    grep -n "minmax(" -r src

    A minmax lower bound larger than the viewport width is the most common source of overflow.

  4. 4Check whether dense broke the focus order.

    grep -n "dense" -r src

    Where dense is present, confirm DOM order matches visual order.

  5. 5Review column-collapse rules at the breakpoint uniformly.

    grep -n "max-width: 768px" -r src

    Confirm each multi-column container has a matching single-column override.

  6. 6Verify every implicit row has a height set.

    grep -n "grid-auto-rows" -r src

    Without it row heights are content-driven, the root of uneven card-wall heights.

Command Examples

Named areas for a whole-page layout

.layout { display: grid; grid-template-areas: "header header" "sidebar main" "footer footer"; grid-template-columns: 200px 1fr; gap: 16px; } .header { grid-area: header; } .sidebar { grid-area: sidebar; } .main { grid-area: main; } .footer { grid-area: footer; }

Named areas separate layout from placement: the parent declares the structure once and children only announce a name, so visual order survives HTML reordering. That pays off in responsive work: collapse the template to one column on small screens and give each area a single grid-area, with no child changes at all.

auto-fit cards with no media query

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; }

auto-fit drops columns as the container narrows and collapses empty tracks, distributing the leftover space to the remaining ones, so cards always fill the width; auto-fill keeps empty tracks reserved, leaving a gap at the end of a row. Use auto-fill when the last row should not stretch.

minmax(0, 1fr) stops content from bursting the track

.grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }

A 1fr track has an automatic minimum, meaning it must still fit its content; a long URL or nowrap string blows the track past the container and breaks the layout. minmax(0, 1fr) explicitly allows shrinking to zero and lets overflow handling inside do the work — the standard fix for overflow in a two-column grid.

Line-based placement for spanning items

.card { grid-column: 1 / -1; /* span the full row */ } .wide { grid-column: 1 / 3; /* span 2 columns */ } .tall { grid-row: 1 / span 3; /* start at row 1 and span 3 rows */ }

Lines are 1-based and -1 means the last line. grid-column: 1 / -1 is the sturdier way to say "span the full row": unlike span N it survives a template change from three columns to two, whereas span 3 would extend past the grid.

Implicit tracks and dense packing

.gallery { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: minmax(150px, auto); grid-auto-flow: row dense; }

When children outnumber the explicit columns, implicit tracks appear with a height fixed by grid-auto-rows, which clips content; minmax(150px, auto) gives a floor while letting content grow. row dense backfills later items into earlier holes for a tidier look, but visual order then differs from DOM order, which matters for keyboard and screen-reader navigation.

Common Pitfalls

  • A fr track has an automatic minimum: a long URL, non-wrapping text, or a fixed-width image pushes the track past the container and produces a horizontal scrollbar. Use minmax(0, 1fr) instead of 1fr in any layout of two columns or more.
  • The strings in grid-template-areas must form rectangles with an identical column count per row. Missing one cell makes the browser drop the whole rule and the layout silently collapses to normal block flow, which looks like "I added areas and nothing happened".
  • align-items / justify-items position items inside their cells, while justify-content / align-content position the whole grid inside the container; the two pairs are unrelated. If the grid will not centre and you keep writing justify-items: center, that is the wrong property.
  • After display: grid, child margins no longer inflate track sizing because tracks are computed from the content box, so layering large margins produces phantom gaps. Express spacing with gap and stop stretching layouts with margins.
  • Browsers without grid support collapse the page to single-column flow rather than erroring. If IE or old Android WebView still matter, wrap the rules in @supports (display: grid) and provide a flex fallback.
  • grid-auto-flow: dense breaks the correspondence between visual and DOM order, so Tab focus jumps around and screen readers announce a different sequence than the eye sees. Reserve it for purely decorative galleries with no focusable interactive children.

Tips

  • fr is a flexible unit — 1fr 2fr means the second column is twice as wide as the first.
  • minmax(200px, 1fr) means min 200px, max flexible fill; pair with auto-fill for responsiveness.
  • grid-template-areas defines layout visually, improving code readability.
  • gap replaces the old grid-gap and is the current standard syntax.
  • auto-fit collapses empty tracks to stretch and fill; auto-fill keeps them — responsive layouts usually use auto-fit.

FAQ

What is the actual difference between auto-fill and auto-fit, and which one for responsive columns?

Both generate responsive columns via repeat(auto-xxx, minmax(200px, 1fr)); the difference shows up when the container has spare width. auto-fill keeps the empty tracks, so when you have fewer cards than columns the widths stay put and the row looks padded on both sides. auto-fit collapses the empty tracks and hands their space to the remaining ones, filling the row. Use auto-fit for fluid card grids whose item count varies, and auto-fill when a dashboard list needs a fixed-width column skeleton.

Content keeps blowing out my 1fr tracks — how do I fix it?

A 1fr track has an automatic minimum size, so a long unbreakable child can stretch it beyond the intended ratio. Writing minmax(0, 1fr) minmax(0, 1fr) forces the minimum to zero so the tracks divide space exactly as specified, and you handle the long content with wrapping or overflow instead. The same applies to grid-template-columns: 200px 1fr — swap that 1fr for minmax(0, 1fr) whenever the main column may contain wide content.

Should I use grid-template-areas or manual grid-column spans?

It depends on whether the layout shape is fixed. For stable page skeletons use grid-template-areas: "header header" "sidebar main" "footer footer" and let each child declare grid-area: header — the stylesheet then reads like a diagram and stays easy to maintain. For content whose position changes constantly and needs irregular placement, address the lines directly: grid-area: 1 / 1 / 3 / 3 is row-start / column-start / row-end / column-end, grid-column: 1 / -1 spans the full row, and grid-row: 1 / span 2 means two rows starting at line 1.

How do justify-items and justify-content differ, and which one do I edit?

They act at different levels. justify-items aligns the child inside each grid cell; justify-content aligns the whole set of tracks inside the container. If the content of a cell is not centered, change justify-items: center or place-items: center. If the tracks themselves are not centered in the container, change justify-content: center or place-content: center. The mental order is: align the tracks (content) first, then align what sits inside each cell (items).

Auto columns get squeezed into a sliver on small screens — how do I stop the horizontal overflow?

Use two levers together. First, wrap the minimum in min() as a floor: repeat(auto-fit, minmax(min(100%, 250px), 1fr)), so the track can never exceed the container width no matter how narrow the viewport gets. Second, at narrow breakpoints collapse to a single column with @media (max-width: 768px) { grid-template-columns: 1fr }, and reorder your grid-template-areas at the same time if needed. For row sizing, grid-auto-rows: minmax(150px, auto) lets rows grow with their content.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us