The grid that
holds the page.
Layout is the contract between a screen and the components on it. Get the grid, the breakpoints, and the four composition primitives right — and every page reads as the work of one team instead of fifty.
01 — Why a grid
Constraint is what makes composition possible.
A grid is a set of vertical lines you commit to using. Twelve is the default because it divides cleanly into 2, 3, 4, and 6 — every common layout (full-bleed, halves, thirds, quarters) snaps to it without remainder. The constraint isn't a limitation; it's the reason ten people can ship pages that look like one designer made them.
The grid lives at the page level. Components don't care about columns — they care about the parent that places them. Stack, Cluster, Grid, and Sidebar are the four primitives that turn columns into composition.
02 — Twelve columns
Twelve divides into everything.
The twelve-column grid is convention because it composes well: 12 ÷ 2 = halves, ÷ 3 = thirds, ÷ 4 = quarters, ÷ 6 = sixths. Almost every editorial, marketing, and product layout maps to one of these splits — and 12-column markup reads as intent (col-span-8 + col-span-4), not as math.
12-column grid · gutter 32px
1200px container
03 — Breakpoints
Five widths cover every device.
Modern breakpoints come from Tailwind, Bootstrap, and Material — they've converged on the same five widths because that's where real devices cluster. Use tokens, never raw pixel values. The grid reflows at each step: 4 columns on mobile, 8 on tablet, 12 from laptop up.
Mobile
--bp-sm
Tablet
--bp-md
Laptop
--bp-lg
Desktop
--bp-xl
Wide
--bp-2xl
Mobile-first. Default styles target the smallest width. Each breakpoint adds — never subtracts. A media query that turns something off is a sign the base case was wrong.
04 — Container widths
Width is content-type, not page-type.
A single container width forces marketing pages, prose, and dashboards into the same shape. Different content has different optimal widths — prose at 65ch, marketing hero at 1280px, dashboards full-bleed. The container is a content property, not a page property.
05 — Composition primitives
Four primitives compose every product layout.
From Heydon Pickering's Every Layout: ninety percent of the layouts a product ever needs are some composition of Stack, Cluster, Grid, and Sidebar. Build them once as primitive components — the rest of the system reads as nesting.
01
Stack
Children stack with consistent gap. The most common layout in any product surface — paragraphs, form fields, card lists.
display: flex; flex-direction: column; gap: var(--space-4)02
Cluster
Children wrap when they run out of room. For tag rows, toolbars, button groups — anything that's a collection of small things.
display: flex; flex-wrap: wrap; gap: var(--space-2)03
Grid
Cells of equal width that reflow at breakpoints. Card grids, image galleries, dashboard tiles.
display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))04
Sidebar
One fixed column, one fluid column. Marketing pages, docs pages, settings — anywhere a navigation rail meets content.
display: grid; grid-template-columns: 240px 1frHeuristic
The parent owns the layout. Components never set their own margins — a Card knows nothing about being in a Stack. That's why the same Card composes inside a Grid, a Cluster, or a Sidebar without modification.
06 — Anti-patterns
Four ways a layout system fights itself.
Pulled from real design-system audits. Each one feels right week one — and breaks the system month six.
Fixed-width containers
Avoid
max-width: 1200px on every pagePrefer
max-width per content type · prose 65ch · full-bleed editorialMarketing pages, prose, and dashboards have different optimal widths. One container width forces everything into a shape it doesn't want — wide marketing or narrow dashboards.
Layouts inside components
Avoid
Card has 32px margin-topPrefer
Card sits inside a Stack with gap: 32pxComponents shouldn't know what's around them. Layout belongs to the parent so the child stays composable in every context — no margin override, no last-child hack.
Magic-number breakpoints
Avoid
@media (min-width: 873px)Prefer
@media (min-width: var(--bp-md))Cherry-picked breakpoints fragment the system. A handful of tokens cover every real device — adding a custom width for one page becomes the start of a maintenance nightmare.
Mobile as a separate site
Avoid
Hide the desktop nav and ship m.example.comPrefer
Same components · same data · responsive at the layout layerTwo codebases, two source-of-truths, two bugs. Modern layouts shrink and reflow — the same Stack and Grid primitives work at every width with breakpoint-aware sizing.
Layout belongs to the parent. Components ship margin-free.
Container width is per content type, not per page.
Compose Stack · Cluster · Grid · Sidebar — don't reinvent.
Continue
Layout decides shape. Accessibility decides reach.
A pixel-perfect grid that nobody can use isn't a system. The accessibility foundation covers the four WCAG principles, keyboard navigation, semantic HTML, and screen-reader testing — the work that makes the layout reach everyone.