You now know every major CSS Grid property. In this final lesson you will put it all together โ building four production-quality layouts from scratch: a responsive dashboard, a photo masonry gallery, a full-page app shell, and a pricing page. For each layout you will see which Grid features are chosen and exactly why, reinforcing the decision-making skills that separate confident Grid users from those who still reach for floats or tables.
Choosing Grid vs Flexbox
| Scenario | Best Tool | Reason |
|---|---|---|
| Row of buttons, nav links | Flexbox | One-dimensional; items flow in a line |
| Card grid with equal columns | Grid | Two-dimensional; strict column alignment |
| Page layout (header/sidebar/main) | Grid | Named areas; row+column control simultaneously |
| Media object (image + text) | Flexbox | Single row; alignment + gap is all you need |
| Masonry / magazine layout | Grid | Items span multiple tracks; explicit placement |
| Chat bubble alignment | Flexbox column | Items stack; align-self moves individual bubbles |
| Form layout (label + input pairs) | Grid | Two-column alignment across rows |
Grid Subgrid (Modern)
| Feature | Property | Use Case |
|---|---|---|
| Subgrid columns | grid-template-columns: subgrid |
Child inherits parent column tracks โ aligns across cards |
| Subgrid rows | grid-template-rows: subgrid |
Child rows align with parent row tracks |
| Support | Chrome 117+, Firefox 71+, Safari 16+ | Progressive enhancement โ check support before using |
CSS Grid Shorthand: grid-template
| Longhand | grid-template Shorthand |
|---|---|
grid-template-rows: 60px 1fr 48px; grid-template-columns: 240px 1fr; grid-template-areas: ... |
grid-template: "h h" 60px "n m" 1fr "f f" 48px / 240px 1fr |
grid-template-rows: auto auto 1fr auto (image, tag, body, footer). The 1fr row stretches the body to fill available space, pushing the footer to the bottom โ without needing the outer grid to do any special work.grid-template shorthand is very powerful but also complex and easy to misread. For production codebases with multiple developers, prefer the explicit longhand properties โ they are easier to understand at a glance, easier to update at breakpoints, and less likely to cause merge conflicts in version control.Basic Example โ Four Production Layouts
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Real-World Grid Layouts</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #f1f5f9; padding: 32px; }
.section-title { font-size: 0.75rem; font-weight: 800; text-transform: uppercase;
letter-spacing: 0.08em; color: #64748b; margin: 40px 0 12px; }
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
LAYOUT 1: Holy Grail with named areas
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.holy-grail {
display: grid;
grid-template-columns: 200px 1fr 180px;
grid-template-rows: 52px 1fr 44px;
grid-template-areas:
"header header header"
"nav main aside"
"footer footer footer";
height: 360px;
border-radius: 12px;
overflow: hidden;
border: 1px solid #e2e8f0;
gap: 0;
margin-bottom: 32px;
}
.hg-header { grid-area: header; background: #1e293b; color: white; padding: 0 20px; display: flex; align-items: center; font-weight: 700; font-size: 0.9rem; }
.hg-nav { grid-area: nav; background: #f8fafc; border-right: 1px solid #e2e8f0; padding: 16px 0; }
.hg-main { grid-area: main; background: white; padding: 24px; overflow-y: auto; }
.hg-aside { grid-area: aside; background: #f8fafc; border-left: 1px solid #e2e8f0; padding: 16px; }
.hg-footer { grid-area: footer; background: #1e293b; color: #94a3b8; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; font-size: 0.75rem; }
.hg-navlink { display: block; padding: 8px 16px; font-size: 0.8rem; color: #475569; text-decoration: none; }
.hg-navlink:hover { background: #ede9fe; color: #4f46e5; }
.hg-main h2 { font-size: 1rem; margin-bottom: 10px; color: #0f172a; }
.hg-main p { font-size: 0.85rem; color: #64748b; line-height: 1.6; }
.hg-aside p { font-size: 0.8rem; color: #64748b; line-height: 1.5; }
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
LAYOUT 2: Responsive card grid
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 16px;
margin-bottom: 32px;
}
.grid-card {
background: white;
border-radius: 12px;
border: 1px solid #e2e8f0;
padding: 20px;
display: grid;
grid-template-rows: auto 1fr auto;
gap: 8px;
}
.grid-card-icon { font-size: 1.5rem; }
.grid-card-title { font-size: 0.95rem; font-weight: 700; color: #0f172a; }
.grid-card-desc { font-size: 0.8rem; color: #64748b; line-height: 1.5; }
.grid-card-link { font-size: 0.8rem; color: #4f46e5; font-weight: 600; text-decoration: none; }
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
LAYOUT 3: Magazine grid
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.magazine {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-auto-rows: 120px;
gap: 8px;
margin-bottom: 32px;
}
.mag-item {
border-radius: 8px;
display: flex;
align-items: flex-end;
padding: 12px;
font-size: 0.75rem;
font-weight: 700;
color: white;
}
.mag-lead { grid-column: span 4; grid-row: span 2; background: linear-gradient(135deg,#4f46e5,#7c3aed); font-size: 1rem; }
.mag-a { grid-column: span 2; background: linear-gradient(135deg,#0891b2,#0284c7); }
.mag-b { grid-column: span 2; background: linear-gradient(135deg,#10b981,#059669); }
.mag-c { grid-column: span 2; background: linear-gradient(135deg,#f59e0b,#d97706); }
.mag-d { grid-column: span 3; background: linear-gradient(135deg,#ef4444,#dc2626); }
.mag-e { grid-column: span 3; background: linear-gradient(135deg,#8b5cf6,#7c3aed); }
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
LAYOUT 4: Pricing grid with subgrid-like alignment
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
align-items: start;
}
.pricing-card {
background: white;
border-radius: 16px;
border: 2px solid #e2e8f0;
padding: 28px;
display: grid;
grid-template-rows: auto auto 1fr auto;
gap: 16px;
}
.pricing-card.featured { border-color: #4f46e5; background: #fafafe; }
.pricing-plan { font-size: 0.7rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em; color: #4f46e5; }
.pricing-price { font-size: 2.5rem; font-weight: 800; color: #0f172a; line-height: 1; }
.pricing-price span { font-size: 1rem; font-weight: 400; color: #64748b; }
.pricing-features { list-style: none; font-size: 0.85rem; color: #475569; display: grid; gap: 8px; }
.pricing-features li::before { content: "โ "; color: #10b981; font-weight: 700; }
.pricing-cta { display: block; text-align: center; padding: 12px; background: #4f46e5; color: white; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 0.875rem; }
</style>
</head>
<body>
<p class="section-title">1 โ Holy Grail: grid-template-areas</p>
<div class="holy-grail">
<header class="hg-header">MyApp Dashboard</header>
<nav class="hg-nav">
<a class="hg-navlink" href="#">Dashboard</a>
<a class="hg-navlink" href="#">Projects</a>
<a class="hg-navlink" href="#">Reports</a>
</nav>
<main class="hg-main">
<h2>Holy Grail Layout</h2>
<p>Built with <code>grid-template-areas</code> โ header, nav, main, aside, footer defined as named regions. The HTML is clean semantic markup; CSS draws the layout map.</p>
</main>
<aside class="hg-aside"><p><strong>Sidebar</strong><br>aside: grid-area: aside</p></aside>
<footer class="hg-footer"><span>© 2025</span><span>CSS Grid Demo</span></footer>
</div>
<p class="section-title">2 โ Auto-responsive card grid</p>
<div class="card-grid">
<div class="grid-card"><span class="grid-card-icon">📈</span><h3 class="grid-card-title">Analytics</h3><p class="grid-card-desc">Track key metrics and visualise trends across your entire platform.</p><a class="grid-card-link" href="#">View reports →</a></div>
<div class="grid-card"><span class="grid-card-icon">👥</span><h3 class="grid-card-title">Team</h3><p class="grid-card-desc">Manage members, roles, and permissions for your organisation.</p><a class="grid-card-link" href="#">Manage team →</a></div>
<div class="grid-card"><span class="grid-card-icon">🔒</span><h3 class="grid-card-title">Security</h3><p class="grid-card-desc">Configure two-factor auth, API keys, and audit logs for your account.</p><a class="grid-card-link" href="#">Security settings →</a></div>
<div class="grid-card"><span class="grid-card-icon">⚙</span><h3 class="grid-card-title">Integrations</h3><p class="grid-card-desc">Connect your workflow with Slack, GitHub, Jira and 50+ other tools.</p><a class="grid-card-link" href="#">Browse integrations →</a></div>
</div>
<p class="section-title">3 โ Magazine grid with explicit placement</p>
<div class="magazine">
<div class="mag-item mag-lead">Lead story โ col span 4, row span 2</div>
<div class="mag-item mag-a">Feature A</div>
<div class="mag-item mag-b">Feature B</div>
<div class="mag-item mag-c">Feature C</div>
<div class="mag-item mag-d">Wide D โ span 3</div>
<div class="mag-item mag-e">Wide E โ span 3</div>
</div>
<p class="section-title">4 โ Pricing cards with pinned CTA</p>
<div class="pricing-grid">
<div class="pricing-card">
<div class="pricing-plan">Starter</div>
<div class="pricing-price">$0<span> / mo</span></div>
<ul class="pricing-features"><li>5 projects</li><li>1 GB storage</li><li>Email support</li></ul>
<a href="#" class="pricing-cta">Get started</a>
</div>
<div class="pricing-card featured">
<div class="pricing-plan">Pro โ Most Popular</div>
<div class="pricing-price">$19<span> / mo</span></div>
<ul class="pricing-features"><li>Unlimited projects</li><li>20 GB storage</li><li>Priority support</li><li>Advanced analytics</li><li>Custom domains</li></ul>
<a href="#" class="pricing-cta">Start free trial</a>
</div>
<div class="pricing-card">
<div class="pricing-plan">Enterprise</div>
<div class="pricing-price">$49<span> / mo</span></div>
<ul class="pricing-features"><li>Everything in Pro</li><li>100 GB storage</li><li>SSO & SAML</li><li>24/7 phone support</li></ul>
<a href="#" class="pricing-cta">Contact sales</a>
</div>
</div>
</body>
</html>
How It Works
Step 1 โ Holy Grail Uses Zero Line Numbers
Every element finds its place via grid-area: name โ no line numbers, no span calculations, no fragile offsets. Changing the layout at a breakpoint means only rewriting the areas string and column definition. The semantic HTML never changes.
Step 2 โ Auto-fill Card Grid Handles Responsiveness Automatically
repeat(auto-fill, minmax(240px, 1fr)) creates as many 240px+ columns as fit. Each card uses an internal grid with grid-template-rows: auto auto 1fr auto โ the description row gets 1fr to absorb height differences, keeping the “learn more” link consistently aligned across all cards.
Step 3 โ Magazine Uses Explicit Placement for Asymmetry
The lead story spans 4 columns and 2 rows using grid-column: span 4; grid-row: span 2. The remaining items auto-place into the 2-column space on the right and then into the next full row. The six-column base grid provides enough granularity for flexible spans.
Step 4 โ Pricing Cards Pin CTA via Internal Row Grid
Each pricing card is a 4-row grid: auto auto 1fr auto. The feature list row gets 1fr โ it stretches to fill all surplus height. The CTA button is in the last auto row, always at the card’s bottom regardless of feature count. All CTAs align perfectly across cards.
Step 5 โ Grid + Flexbox Together
The header and footer of the holy grail layout use display: flex internally for their alignment needs (logo left, user right). CSS Grid handles the page structure; Flexbox handles component-level alignment within each grid area โ the two systems complement each other perfectly.
Real-World Example: Responsive Form Layout
/* form-grid.css */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #f8fafc; padding: 40px 24px; }
.form-card {
background: white;
border-radius: 16px;
border: 1px solid #e2e8f0;
padding: 32px;
max-width: 640px;
margin: 0 auto;
box-shadow: 0 4px 16px rgba(0,0,0,0.06);
}
.form-title { font-size: 1.25rem; font-weight: 700; margin-bottom: 24px; color: #0f172a; }
/* โโ Grid form layout โโ */
.form-grid {
display: grid;
grid-template-columns: 1fr; /* single column mobile */
gap: 16px;
}
@media (min-width: 520px) {
.form-grid { grid-template-columns: 1fr 1fr; } /* two-column tablet+ */
}
/* Fields that should span full width */
.field-full { grid-column: 1 / -1; }
/* Label + input group */
.field { display: grid; gap: 6px; }
label {
font-size: 0.8rem; font-weight: 600;
color: #374151; letter-spacing: 0.01em;
}
input, select, textarea {
width: 100%;
padding: 10px 12px;
border: 1.5px solid #e2e8f0;
border-radius: 8px;
font-size: 0.875rem;
font-family: system-ui, sans-serif;
color: #1e293b;
background: white;
transition: border-color 0.15s;
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: #4f46e5;
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}
textarea { resize: vertical; min-height: 100px; }
/* Form footer with submit button */
.form-footer {
grid-column: 1 / -1;
display: flex;
justify-content: flex-end;
gap: 12px;
padding-top: 8px;
}
.btn-cancel {
padding: 10px 20px; border: 1.5px solid #e2e8f0;
border-radius: 8px; background: white; color: #374151;
font-size: 0.875rem; cursor: pointer; font-family: system-ui, sans-serif;
}
.btn-submit {
padding: 10px 24px; background: #4f46e5;
border: none; border-radius: 8px; color: white;
font-size: 0.875rem; font-weight: 600; cursor: pointer;
font-family: system-ui, sans-serif;
}
Common Mistakes
Mistake 1 โ Using Grid where Flexbox is simpler
โ Wrong โ Grid overkill for a simple horizontal button row:
.btn-group {
display: grid;
grid-template-columns: auto auto auto;
gap: 8px;
}
โ Correct โ Flexbox is cleaner for a simple row of items:
.btn-group { display: flex; gap: 8px; }
Mistake 2 โ Hardcoding column counts instead of using auto-fill
โ Wrong โ breaks at unexpected viewport widths:
.grid { display: grid; grid-template-columns: repeat(4, 1fr); }
@media (max-width: 768px) { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .grid { grid-template-columns: 1fr; } }
โ Correct โ auto-fill handles all widths with one declaration:
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
Mistake 3 โ Not providing grid-auto-rows for dynamic content
โ Wrong โ implicit rows collapse to 0 height when items are taller than the cell:
.grid { display: grid; grid-template-columns: repeat(3, 1fr); }
/* rows are auto-created but have no minimum height โ content overflows */
โ Correct โ define a minimum row height for auto-created rows:
.grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: minmax(100px, auto); }
Quick Reference
| Pattern | Key CSS | Notes |
|---|---|---|
| Responsive card grid | repeat(auto-fill, minmax(240px, 1fr)) |
No media queries needed |
| Holy grail layout | grid-template-areas + named grid-area |
Redefine areas at breakpoints |
| Magazine grid | grid-column: span N; grid-row: span N; |
6-column base for flexibility |
| Pinned card footer | Card: grid 4 rows; body row: 1fr; footer row: auto | Works across unequal content heights |
| Two-column form | grid-template-columns: 1fr 1fr; field-full: 1/-1 |
Single column on mobile via media query |
| Grid + Flexbox | Grid for page structure; Flexbox inside areas | They complement each other โ use both |