Layouts: CSS Grid Basics
CSS Grid is a two-dimensional layout system. It controls rows and columns at the same time.
Use Grid when you need:
- page shells,
- dashboards,
- image galleries,
- card grids,
- complex forms,
- magazine-style layouts,
- or any layout where rows and columns both matter.
Flexbox asks, "How should items flow along one axis?" Grid asks, "What structure should this two-dimensional layout have?"
1. Grid Container and Grid Items
Grid starts with:
.container {
display: grid;
}
<section class="container">
<article>One</article>
<article>Two</article>
<article>Three</article>
</section>
The direct children become grid items.
2. Defining Columns and Rows
The core grid properties are:
.container {
display: grid;
grid-template-columns: 200px 1fr 100px;
grid-template-rows: 100px auto;
gap: 20px;
}
This creates:
- three columns,
- two rows,
- and a
20pxgap between tracks.
Grid tracks are rows or columns. Grid cells are the spaces where rows and columns intersect.
3. The fr Unit
fr means "fraction of available space."
.three-columns {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
This creates three equal columns.
.main-layout {
display: grid;
grid-template-columns: 240px 1fr;
}
The first column is fixed at 240px. The second receives the remaining space.
4. repeat()
repeat() avoids repeating track definitions manually.
.grid {
grid-template-columns: repeat(12, 1fr);
}
This is equivalent to:
.grid {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}
You can mix fixed and repeated tracks:
.layout {
grid-template-columns: 240px repeat(3, 1fr);
}
5. minmax()
minmax(min, max) defines a track that cannot be smaller than the minimum or larger than the maximum.
.cards {
display: grid;
grid-template-columns: repeat(3, minmax(200px, 1fr));
gap: 1rem;
}
Each column is at least 200px, but can grow to share available space.
This is useful for card layouts because it prevents columns from becoming too narrow.
6. auto-fit and auto-fill
For responsive grids, combine repeat(), auto-fit, and minmax().
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1rem;
}
This creates as many columns as fit. When the screen gets smaller, the layout automatically reduces the number of columns.
auto-fit collapses empty tracks. auto-fill keeps empty tracks reserved. In practical card grids, auto-fit is often what you want.
7. Gap
gap controls space between rows and columns.
.grid {
display: grid;
gap: 1rem;
}
You can set row and column gaps separately:
.grid {
row-gap: 2rem;
column-gap: 1rem;
}
Or shorthand:
.grid {
gap: 2rem 1rem;
}
The first value is row gap. The second is column gap.
8. Explicit and Implicit Grids
The explicit grid is what you define:
.grid {
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 120px 120px;
}
If there are more items than defined cells, the browser creates implicit rows or columns.
.grid {
grid-auto-rows: minmax(120px, auto);
}
Use grid-auto-rows to control automatically created rows.
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 180px;
gap: 1rem;
}
9. Grid Lines
Grid lines are numbered. A three-column grid has four vertical column lines.
.feature {
grid-column: 1 / 3;
}
This means the item starts at column line 1 and ends at column line 3.
.hero {
grid-column: 1 / -1;
}
-1 means the last line, so this spans the full width.
10. Spanning
Use span when you care about how many tracks an item covers.
.wide {
grid-column: span 2;
}
.tall {
grid-row: span 2;
}
This is useful in dashboards, galleries, and content layouts.
.dashboard-card.featured {
grid-column: span 2;
grid-row: span 2;
}
11. Grid Template Areas
Template areas let you name parts of a layout.
.page {
display: grid;
grid-template-columns: 260px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
.page-header {
grid-area: header;
}
.page-sidebar {
grid-area: sidebar;
}
.page-main {
grid-area: main;
}
.page-footer {
grid-area: footer;
}
Template areas are readable and excellent for page-level layouts.
12. Responsive Template Areas
You can change the grid layout at breakpoints.
.page {
display: grid;
gap: 1rem;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
@media (min-width: 800px) {
.page {
grid-template-columns: 260px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
}
This keeps mobile layout simple and expands into a two-column layout on larger screens.
13. Alignment in Grid
Grid has two levels of alignment.
Align Items Inside Cells
.grid {
justify-items: center;
align-items: center;
}
justify-items: horizontal alignment inside each cell.align-items: vertical alignment inside each cell.
Align the Whole Grid
.grid {
justify-content: center;
align-content: center;
}
justify-content: moves the grid tracks horizontally when there is extra container space.align-content: moves the grid tracks vertically when there is extra container space.
Align One Item
.special {
justify-self: end;
align-self: start;
}
Use self properties for one item.
14. Dense Packing
grid-auto-flow: dense lets the browser fill holes in the grid when later items fit.
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
grid-auto-flow: dense;
}
Be careful. Dense packing can visually reorder items. If the order matters for reading or accessibility, avoid dense packing.
15. Common Pattern: Responsive Card Grid
.course-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1.25rem;
}
<section class="course-grid">
<article>JavaScript</article>
<article>TypeScript</article>
<article>Python</article>
</section>
This is one of the most useful Grid patterns. It needs no media query for basic responsiveness.
16. Common Pattern: Holy Grail Layout
.app-shell {
min-height: 100vh;
display: grid;
grid-template-rows: auto 1fr auto;
}
<div class="app-shell">
<header>Header</header>
<main>Main content</main>
<footer>Footer</footer>
</div>
The middle row grows to fill available space, pushing the footer to the bottom.
17. Common Pattern: Dashboard
.dashboard {
display: grid;
grid-template-columns: 260px minmax(0, 1fr);
gap: 1.5rem;
}
.metrics {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1rem;
}
minmax(0, 1fr) prevents long content from forcing the main column wider than the viewport.
This is similar to using min-width: 0 in Flexbox.
18. Grid vs. Flexbox
Grid and Flexbox are complementary.
| Layout problem | Recommended tool |
|---|---|
| Navigation links in a row | Flexbox |
| Center icon and text in a button | Flexbox |
| Responsive card gallery | Grid |
| Full page header/sidebar/main/footer | Grid |
| Toolbar with variable buttons | Flexbox |
| Form with aligned labels and fields | Grid |
| Media object with image and text | Flexbox |
Grid can do one-dimensional layouts, and Flexbox can sometimes imitate grids, but using the right tool keeps CSS simpler.
19. Debugging Grid
When a grid layout behaves unexpectedly, ask:
- Which element has
display: grid? - Are the intended grid items direct children?
- What columns and rows are explicit?
- Are implicit rows being created?
- Is
gappart of the available space calculation? - Is an item spanning more tracks than expected?
- Are grid lines counted correctly?
- Would
minmax(0, 1fr)prevent overflow? - Is dense packing changing visual order?
- Would template areas make the layout easier to read?
20. Mini Practice
Create a responsive course card grid:
- Each card should be at least
240px. - Cards should expand to fill available space.
- The grid should use a
1remgap. - It should work without a media query.
Possible answer:
.courses {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1rem;
}
Create a page shell:
- Header spans full width.
- Sidebar and main content sit side by side on desktop.
- Footer spans full width.
- Layout stacks on mobile.
Possible answer:
.page {
display: grid;
gap: 1rem;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
.header {
grid-area: header;
}
.sidebar {
grid-area: sidebar;
}
.main {
grid-area: main;
}
.footer {
grid-area: footer;
}
@media (min-width: 800px) {
.page {
grid-template-columns: 260px minmax(0, 1fr);
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
}