Master the 12-column grid system used by every major design system and UI framework, and implement responsive column-based layouts using CSS Grid with named grid lines, area templates, and auto-placement algorithms.
The 12-column grid is one of the most consequential ideas in visual design. Developed in the Swiss typographic tradition and popularized in web design through Bootstrap, it provides a flexible mathematical framework for dividing horizontal space into any combination of columns that sum to 12: full width (12), halves (6+6), thirds (4+4+4), quarters (3+3+3+3), and mixed (8+4, 9+3, 7+5).
Every major design system uses a 12-column grid: Bootstrap, Tailwind CSS, Material Design, Ant Design, and the GitHub Primer. When you understand the 12-column grid, you can read any designer's layout annotation and implement it precisely in CSS without needing clarification.
CSS Grid is the native implementation of the 12-column grid — and much more. It can create any grid, named regions, overlapping content, and asymmetric layouts that were impossible with float-based or flex-based systems. This lesson bridges the gap from Figma column annotations to production CSS Grid code.
The number 12 has exceptional mathematical divisibility: it divides evenly by 1, 2, 3, 4, 6, and 12. This means a 12-column layout can accommodate:
No other small number offers this flexibility. 10 cannot divide into thirds; 16 cannot divide into thirds or sixths.
The anatomy of a 12-column grid:
Implementing the 12-column grid in CSS Grid:
1/* Classic 12-column grid container */
2.grid-12 {
3 display: grid;
4 grid-template-columns: repeat(12, 1fr); /* 12 equal columns */
5 column-gap: 1.5rem; /* 24px gutter */
6 max-width: 1280px;
7 margin-inline: auto; /* center on page */
8 padding-inline: 1.5rem; /* edge margins */
9 width: 100%;
10}
11
12/* Span classes — items span specific column counts */
13.col-1 { grid-column: span 1; }
14.col-2 { grid-column: span 2; }
15.col-3 { grid-column: span 3; }
16.col-4 { grid-column: span 4; }
17.col-6 { grid-column: span 6; }
18.col-8 { grid-column: span 8; }
19.col-9 { grid-column: span 9; }
20.col-12 { grid-column: span 12; }
21
22/* Example: 8+4 layout (main content + sidebar) */
23.main-content { grid-column: span 8; }
24.sidebar { grid-column: span 4; }
25
26/* Responsive: full width on mobile, 12 columns on desktop */
27@media (max-width: 768px) {
28 .grid-12 { grid-template-columns: 1fr; }
29 [class*='col-'] { grid-column: span 1; } /* all spans go full width */
30}Grid template areas allow you to define the layout as ASCII art — naming each region and assigning HTML elements to those regions. This is the most readable way to implement complex page layouts and the most direct translation of a Figma page structure annotation.
Designer annotation: 'Standard page layout: full-width header, 240px sidebar on left, main content area on right, footer full-width.'
CSS Grid implementation:
1/* Named region layout from designer annotation */
2.page-layout {
3 display: grid;
4 min-height: 100vh;
5 grid-template-rows: auto 1fr auto; /* header: auto, main: stretch, footer: auto */
6 grid-template-columns: 240px 1fr; /* fixed sidebar, flexible main */
7 grid-template-areas:
8 'header header' /* header spans both columns */
9 'sidebar main ' /* sidebar left, main right */
10 'footer footer'; /* footer spans both columns */
11}
12
13/* Assign elements to named regions */
14.site-header { grid-area: header; }
15.site-sidebar { grid-area: sidebar; }
16.site-main { grid-area: main; }
17.site-footer { grid-area: footer; }
18
19/* Mobile: stack everything vertically */
20@media (max-width: 768px) {
21 .page-layout {
22 grid-template-columns: 1fr; /* single column */
23 grid-template-areas:
24 'header'
25 'main' /* sidebar moves below main on mobile */
26 'sidebar'
27 'footer';
28 }
29}More complex dashboard layout with named areas:
1.dashboard-layout {
2 display: grid;
3 min-height: 100vh;
4 grid-template-columns: 220px 1fr;
5 grid-template-rows: 3.5rem 1fr auto;
6 grid-template-areas:
7 'nav header'
8 'nav main '
9 'nav footer';
10}
11
12.dashboard-nav { grid-area: nav; }
13.dashboard-header { grid-area: header; }
14.dashboard-main { grid-area: main; }
15.dashboard-footer { grid-area: footer; }
16
17/* Named grid lines (alternative to template areas) */
18.explicit-grid {
19 display: grid;
20 grid-template-columns:
21 [full-start] 1rem
22 [content-start] 1fr
23 [content-end] 1rem
24 [full-end];
25}
26
27/* Item can break out of content column to full width */
28.full-width { grid-column: full-start / full-end; }
29.content { grid-column: content-start / content-end; }The CSS Grid auto-placement algorithm combined with repeat(auto-fill, minmax()) creates responsive card grids that adapt to any viewport width without a single media query. This is the most powerful and commonly used pattern for content grids.
The auto-fill/minmax pattern:
1/* Responsive card grid — no media queries needed */
2.card-grid {
3 display: grid;
4 grid-template-columns: repeat(
5 auto-fill, /* fill with as many columns as fit */
6 minmax(280px, 1fr) /* each column: min 280px, max equal share */
7 );
8 gap: 1.5rem;
9}How it works:
auto-fill: create as many columns as fit within the container widthminmax(280px, 1fr): each column is at least 280px wide and at most one equal share of the remaining spaceauto-fit vs auto-fill:
auto-fill: empty columns are created to maintain the grid structureauto-fit: empty columns collapse to zero width — items expand to fill space1/* auto-fill: maintains empty column positions */
2.auto-fill-grid {
3 grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
4 /* On wide screen with 3 items: creates 6 columns, 3 filled, 3 empty */
5}
6
7/* auto-fit: collapses empty columns */
8.auto-fit-grid {
9 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
10 /* On wide screen with 3 items: 3 items expand to fill all 3 columns */
11}
12
13/* Fixed max columns with flexibility: 3 max, responsive below */
14.max-3-grid {
15 display: grid;
16 grid-template-columns: repeat(auto-fill, minmax(max(240px, 30%), 1fr));
17 gap: 1.5rem;
18}For cases where you need explicit control of column count at breakpoints:
1/* Explicit breakpoint control when auto-fill is not sufficient */
2.feature-grid {
3 display: grid;
4 grid-template-columns: 1fr; /* 1 column: mobile */
5 gap: 1.5rem;
6}
7@media (min-width: 640px) {
8 .feature-grid { grid-template-columns: repeat(2, 1fr); } /* 2 col: tablet */
9}
10@media (min-width: 1024px) {
11 .feature-grid { grid-template-columns: repeat(3, 1fr); } /* 3 col: desktop */
12}Figma uses a column grid system that maps directly to CSS Grid. When inspecting a Figma frame with Dev Mode enabled, you see column count, gutter width, margin, and container width. Here is how to translate each Figma grid setting to CSS:
Figma grid settings → CSS Grid translation:
| Figma setting | CSS Grid property |
|---|---|
| Count: 12 | grid-template-columns: repeat(12, 1fr) |
| Gutter: 24 | column-gap: 1.5rem |
| Margin: 80 | padding-inline: 5rem |
| Width: 1440 | max-width: 90rem |
| Type: Center | margin-inline: auto |
Figma component span annotation → CSS:
If a Figma component spans columns 1 through 8 of a 12-column grid, CSS is:
grid-column: 1 / 9; (start at column line 1, end at column line 9)
Or using span notation:
grid-column: span 8; (span 8 columns from current position)
Full translation of a common Figma course detail page layout:
1/* Figma annotation: 12-col grid, 24px gutter, 80px margin, 1280px max-width */
2.course-page {
3 display: grid;
4 grid-template-columns: repeat(12, 1fr);
5 column-gap: 1.5rem; /* 24px gutter */
6 max-width: 80rem; /* 1280px */
7 margin-inline: auto;
8 padding-inline: 5rem; /* 80px margin */
9 align-items: start;
10}
11
12/* Figma: course content spans cols 1-8, sidebar spans cols 9-12 */
13.course-content { grid-column: span 8; }
14.course-sidebar { grid-column: span 4; }
15
16/* Figma: on tablet, content full width, sidebar below */
17@media (max-width: 1024px) {
18 .course-page {
19 padding-inline: 2rem;
20 row-gap: 2rem;
21 }
22 .course-content { grid-column: span 12; }
23 .course-sidebar { grid-column: span 12; }
24}
25
26/* Figma: on mobile, single column, minimal padding */
27@media (max-width: 640px) {
28 .course-page { padding-inline: 1rem; }
29}A complete dashboard page implementing a 12-column grid with named template areas, a sticky sidebar, and a responsive card grid that adapts from 1 to 4 columns.
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Dashboard — Grid Layout Demo</title>
7 <style>
8 :root {
9 --color-nav-bg: #1e293b;
10 --color-nav-text: #94a3b8;
11 --color-nav-active: #f8fafc;
12 --color-bg: #f1f5f9;
13 --color-surface: #ffffff;
14 --color-border: #e2e8f0;
15 --color-text-primary: #0f172a;
16 --color-text-muted: #64748b;
17 --color-brand: #2563eb;
18 --nav-width: 220px;
19 --header-height: 3.5rem;
20 }
21 *, *::before, *::after { box-sizing: border-box; }
22 body { margin: 0; font-family: system-ui, sans-serif;
23 color: var(--color-text-primary); background: var(--color-bg); }
24
25 /* Named area layout — direct from designer annotation */
26 .app-shell {
27 display: grid;
28 min-height: 100vh;
29 grid-template-columns: var(--nav-width) 1fr;
30 grid-template-rows: var(--header-height) 1fr;
31 grid-template-areas:
32 'nav header'
33 'nav main ';
34 }
35
36 /* Sidebar navigation */
37 .app-nav {
38 grid-area: nav;
39 background: var(--color-nav-bg);
40 position: sticky;
41 top: 0;
42 height: 100vh;
43 overflow-y: auto;
44 display: flex;
45 flex-direction: column;
46 padding: 1rem 0.75rem;
47 }
48 .nav-logo { color: white; font-weight: 800; font-size: 1.125rem;
49 padding: 0.5rem 0.75rem; margin-bottom: 1.5rem; }
50 .nav-link {
51 display: flex; align-items: center; gap: 0.625rem;
52 padding: 0.625rem 0.75rem;
53 border-radius: 0.5rem;
54 color: var(--color-nav-text);
55 text-decoration: none;
56 font-size: 0.9375rem;
57 font-weight: 500;
58 transition: background-color 150ms ease, color 150ms ease;
59 margin-bottom: 0.25rem;
60 }
61 .nav-link:hover { background: rgba(255,255,255,0.06); color: var(--color-nav-active); }
62 .nav-link[aria-current="page"] { background: rgba(255,255,255,0.1); color: white; }
63
64 /* Header */
65 .app-header {
66 grid-area: header;
67 background: var(--color-surface);
68 border-bottom: 1px solid var(--color-border);
69 display: flex;
70 align-items: center;
71 padding: 0 1.5rem;
72 gap: 1rem;
73 position: sticky;
74 top: 0;
75 z-index: 50;
76 }
77 .app-header h1 { margin: 0; font-size: 1.125rem; font-weight: 700; }
78
79 /* Main content area */
80 .app-main {
81 grid-area: main;
82 padding: 2rem 1.5rem;
83 overflow-y: auto;
84 }
85
86 /* Stats grid — 4 columns on desktop */
87 .stats-grid {
88 display: grid;
89 grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
90 gap: 1rem;
91 margin-bottom: 2rem;
92 }
93 .stat-card {
94 background: var(--color-surface);
95 border: 1px solid var(--color-border);
96 border-radius: 0.75rem;
97 padding: 1.25rem;
98 }
99 .stat-label { font-size: 0.75rem; font-weight: 700; text-transform: uppercase;
100 letter-spacing: 0.06em; color: var(--color-text-muted); margin: 0 0 0.5rem; }
101 .stat-value { font-size: 2rem; font-weight: 800;
102 color: var(--color-text-primary); line-height: 1; margin: 0; }
103 .stat-delta { font-size: 0.8125rem; color: #16a34a; font-weight: 600; margin-top: 0.375rem; }
104
105 /* Course grid — auto-fill responsive */
106 .section-title { font-size: 1.125rem; font-weight: 700;
107 margin: 0 0 1rem; color: var(--color-text-primary); }
108 .course-grid {
109 display: grid;
110 grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
111 gap: 1.25rem;
112 }
113 .course-card {
114 background: var(--color-surface);
115 border: 1px solid var(--color-border);
116 border-radius: 0.75rem;
117 overflow: hidden;
118 }
119 .course-thumb {
120 aspect-ratio: 16/9;
121 background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
122 display: flex; align-items: center; justify-content: center;
123 font-size: 2rem;
124 }
125 .course-body { padding: 1rem; }
126 .course-title { font-size: 0.9375rem; font-weight: 600;
127 color: var(--color-text-primary); margin: 0 0 0.375rem; }
128 .course-progress-bar {
129 height: 4px; background: var(--color-border);
130 border-radius: 9999px; overflow: hidden; margin-top: 0.625rem;
131 }
132 .course-progress-fill {
133 height: 100%; background: var(--color-brand); border-radius: 9999px;
134 transition: width 500ms ease;
135 }
136
137 /* Mobile responsive */
138 @media (max-width: 768px) {
139 .app-shell {
140 grid-template-columns: 1fr;
141 grid-template-areas: 'header' 'main';
142 }
143 .app-nav { display: none; } /* hide sidebar on mobile (simplified) */
144 }
145 </style>
146</head>
147<body>
148 <div class="app-shell">
149 <!-- Named region: nav -->
150 <nav class="app-nav" aria-label="App navigation">
151 <div class="nav-logo">DevBootstrap</div>
152 <a href="/dashboard" class="nav-link" aria-current="page">Dashboard</a>
153 <a href="/courses" class="nav-link">My Courses</a>
154 <a href="/progress" class="nav-link">Progress</a>
155 <a href="/certificates" class="nav-link">Certificates</a>
156 <a href="/settings" class="nav-link">Settings</a>
157 </nav>
158 <!-- Named region: header -->
159 <header class="app-header">
160 <h1>My Dashboard</h1>
161 </header>
162 <!-- Named region: main -->
163 <main class="app-main">
164 <!-- Stats: auto-fill responsive grid -->
165 <div class="stats-grid">
166 <div class="stat-card">
167 <p class="stat-label">Courses enrolled</p>
168 <p class="stat-value">12</p>
169 <p class="stat-delta">+2 this month</p>
170 </div>
171 <div class="stat-card">
172 <p class="stat-label">Hours learned</p>
173 <p class="stat-value">47</p>
174 <p class="stat-delta">+8h this week</p>
175 </div>
176 <div class="stat-card">
177 <p class="stat-label">Certificates</p>
178 <p class="stat-value">3</p>
179 <p class="stat-delta">2 in progress</p>
180 </div>
181 <div class="stat-card">
182 <p class="stat-label">Streak</p>
183 <p class="stat-value">14</p>
184 <p class="stat-delta">days in a row</p>
185 </div>
186 </div>
187 <!-- Continue learning: card grid -->
188 <h2 class="section-title">Continue Learning</h2>
189 <div class="course-grid">
190 <div class="course-card">
191 <div class="course-thumb">⚛</div>
192 <div class="course-body">
193 <p class="course-title">React Fundamentals</p>
194 <small style="color: var(--color-text-muted);">Lesson 8 of 24</small>
195 <div class="course-progress-bar">
196 <div class="course-progress-fill" style="width: 33%"></div>
197 </div>
198 </div>
199 </div>
200 <div class="course-card">
201 <div class="course-thumb">🎨</div>
202 <div class="course-body">
203 <p class="course-title">CSS Design Systems</p>
204 <small style="color: var(--color-text-muted);">Lesson 3 of 18</small>
205 <div class="course-progress-bar">
206 <div class="course-progress-fill" style="width: 17%"></div>
207 </div>
208 </div>
209 </div>
210 </div>
211 </main>
212 </div>
213</body>
214</html>This dashboard uses two CSS Grid patterns: the outer app-shell uses grid-template-areas with a fixed 220px sidebar and a flexible 1fr main area — the named regions (nav, header, main) eliminate column math entirely and make the layout intent immediately readable from the CSS. The inner stats-grid and course-grid use repeat(auto-fill, minmax()) for responsive card layouts that adapt from 4 columns on desktop to 1 column on mobile without a single media query. On mobile, the grid-template-areas redefine the outer layout to a single column with the sidebar hidden — translating the designer's mobile annotation directly.
The next lesson covers Nielsen's 10 Usability Heuristics — the industry-standard checklist for evaluating interface usability — and how each heuristic translates to specific CSS and HTML implementation decisions.
Your progress is saved in this browser, on this device only.