Website Markup Using CSS Grid Layout
CSS Grid is a two-dimensional layout system that fundamentally changed the approach to complex layouts. Where absolute positioning, JavaScript plugins, or Bootstrap's 12-column grids were previously required, now just a few lines of CSS suffice.
When to Use Grid vs Flexbox
Simple rule: Grid is for two-dimensional layouts (rows AND columns simultaneously), Flexbox is for one-dimensional layouts (rows OR columns).
- Main page layout (header / sidebar / main / footer) — Grid
- Card gallery — Grid
- Navigation list — Flexbox
- Button with icon and text — Flexbox
- Form with fields — either depending on complexity
Page Layout
.layout {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 280px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100dvh;
}
.layout__header { grid-area: header; }
.layout__sidebar { grid-area: sidebar; }
.layout__main { grid-area: main; }
.layout__footer { grid-area: footer; }
@media (max-width: 768px) {
.layout {
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
grid-template-columns: 1fr;
}
}
grid-template-areas is a visual representation of the layout directly in CSS. When changing media queries, you only need to rebuild grid-template-areas without touching child elements.
Responsive Card Grid Without Media Queries
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}
auto-fill + minmax(280px, 1fr) — the grid decides how many columns fit on its own. On mobile — 1 column, on tablet — 2–3, on desktop — 4+. Not a single media query.
The difference between auto-fill vs auto-fit:
-
auto-fill— creates empty tracks if there are fewer items than columns -
auto-fit— empty tracks shrink to zero, items stretch
For a gallery with fixed card sizes — use auto-fill. For a list with stretching items — use auto-fit.
Dense Packing (Masonry-like)
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 10px; /* Small step */
gap: 16px;
}
.gallery__item--tall { grid-row: span 20; }
.gallery__item--short { grid-row: span 12; }
Or CSS-only masonry (Firefox 126+, Chrome with flag):
.masonry {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: masonry;
gap: 16px;
}
It's not supported everywhere yet — production requires a polyfill (JavaScript-based masonry) as a fallback.
Element Alignment
.hero {
display: grid;
place-items: center; /* justify-items + align-items: center in one line */
min-height: 100vh;
}
/* Specific element — push to the right */
.nav__cta {
margin-inline-start: auto; /* or: justify-self: end in grid context */
}
Overlapping Elements
Grid allows you to layer elements on top of each other without position: absolute:
.hero-banner {
display: grid;
grid-template-areas: "content";
}
.hero-banner__image,
.hero-banner__content {
grid-area: content; /* Both occupy one cell — overlap */
}
.hero-banner__image {
width: 100%;
height: 100%;
object-fit: cover;
}
.hero-banner__content {
z-index: 1;
padding: 48px;
align-self: end;
background: linear-gradient(transparent, rgba(0,0,0,0.7));
}
Subgrid
Supported in all modern browsers since 2023. Allows child elements to align with the parent grid:
.product-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.product-card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid; /* Title, description, and button aligned across all cards */
}
.product-card__title { grid-row: 1; }
.product-card__description { grid-row: 2; }
.product-card__action { grid-row: 3; align-self: end; }
Without subgrid, aligning card content by height required JavaScript or workarounds. Now — two CSS properties.
Holy Grail Layout (Example with Real Units)
.page {
display: grid;
grid-template:
"header" 64px
"main" 1fr
"footer" auto
/ 1fr;
}
@media (min-width: 1024px) {
.page {
grid-template:
"header header header" 64px
"nav main aside" 1fr
"footer footer footer" auto
/ 240px 1fr 320px;
}
}
A single grid-template declaration includes grid-template-areas, grid-template-rows, and grid-template-columns.
Common Mistakes
Grid for everything. Grid context is heavier than Flexbox. For a simple row of buttons — Flexbox is faster and simpler.
Explicit rows without auto.
/* Problem — if there's more than 3 rows of content, it will overflow */
grid-template-rows: 200px 100px 300px;
/* Correct */
grid-auto-rows: auto;
/* or */
grid-template-rows: auto 1fr auto;
Ignoring gap in favor of margin. In a grid context, gap is the right tool. margin on child elements breaks alignment.
Timeframe
| Layout Type | Time |
|---|---|
| Simple page (hero + 3–4 sections) | 1 day |
| Complex layout with sidebar + cards | 1.5–2 days |
| Dashboard with multi-column layout | 2–3 days |







