11 KiB
Layout — Grid, Space, Rhythm
Layout is the skeleton the user never sees and always feels. A page with a real grid, a real spacing scale, and real breakpoints reads as designed. A page with guessed margins reads as generated. Layout is decided before the first component is built — see
SKILL.mdStep 5.
The Container System
Decide container widths once, use them everywhere.
| Token | Width | Use |
|---|---|---|
--container-read |
65ch–72ch |
Long-form body text (measure) |
--container-text |
720px |
Article intros, single-column sections |
--container-main |
1200px–1280px |
Default page container (nav, hero, features) |
--container-wide |
1440px |
Index tables, image galleries, data-heavy pages |
| Full bleed | 100% |
One or two moments per page — a spread, a footer, a manifesto |
Rules
- One container per page, plus at most one full-bleed exception. Mixing four content widths per page reads as accidental.
- Side padding:
clamp(20px, 4vw, 48px)minimum;clamp(24px, 6vw, 80px)for editorial and Swiss pages where margins carry the design. - Never let body copy span
--container-main. Text columns cap at ~40ch–45chinside a wide grid; the grid column holds it, not the container. - Content must never touch the viewport edge below 400px — padding scales down, never below 20px.
:root {
--container-main: 1240px;
--container-text: 720px;
--container-read: 68ch;
--pad-inline: clamp(20px, 4vw, 48px);
}
.container {
max-width: var(--container-main);
margin-inline: auto;
padding-inline: var(--pad-inline);
}
The Spacing Scale
One scale. Everything is spaced from it. No margin: 37px, no padding: 22px, no one-off gaps.
:root {
--sp-1: 4px;
--sp-2: 8px;
--sp-3: 12px;
--sp-4: 16px;
--sp-5: 24px;
--sp-6: 32px;
--sp-7: 48px;
--sp-8: 64px;
--sp-9: 96px;
--sp-10: 128px;
}
Section rhythm
| Viewport | Between sections | Inside a section |
|---|---|---|
| Mobile (< 720px) | --sp-7 (48px) |
--sp-5 – --sp-6 |
| Tablet (720–1024px) | --sp-8 (64px) |
--sp-6 |
| Desktop (> 1024px) | --sp-9 – --sp-10 (96–128px) |
--sp-6 – --sp-7 |
Rules:
- Space before a heading is larger than space after it (roughly 1.5–2×). The heading belongs to the text below it — proximity is hierarchy.
- If two sections need a divider and more space, the spacing was wrong. Whitespace separates; hairlines clarify (tables, indices). Not both everywhere.
- Space communicates hierarchy: more space = more importance. The hero gets the most air on the page. If every section has 128px around it, none of them is the hero.
Grid Systems
The default: 12 columns
.grid {
display: grid;
grid-template-columns: repeat(12, minmax(0, 1fr));
column-gap: var(--sp-5);
}
Use it for hero splits and multi-column zones. Not every zone needs all 12 — see splits below.
Editorial / Swiss: 6 columns
Wider gutters, fewer columns, stronger verticals. Index pages, archives, tables of contents. Pair with hairline rules and mono metadata — see editorial-patterns.md.
Asymmetric splits (the anti-slop move)
Equal 50/50 and identical thirds are the default AI output. Offset the split instead:
| Split | Effect | Typical use |
|---|---|---|
5fr / 7fr |
Text-led, support right | Hero: headline left, product/UI right |
3fr / 9fr |
Sidebar + content | Article with meta column, docs |
7fr / 5fr |
Support left, text right | Feature sections alternating with the hero |
4fr / 4fr / 4fr |
Avoid — identical thirds | (Only for genuinely equal data: pricing tiers you've already fixed per anti-patterns.md §13) |
2fr / 6fr / 4fr |
Meta + body + aside | Editorial spreads, catalog entries |
.hero {
display: grid;
grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
column-gap: clamp(32px, 5vw, 80px);
align-items: center;
}
/* Alternate the next feature section — mirror, don't repeat */
.feature--flipped { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
The meta-column pattern
A workhorse: a narrow fixed column (180px–220px) for labels, numbers, kickers; the rest for content. It forces asymmetry, gives metadata a home, and scales down to one column on mobile. Used by Pentagram archives, product docs, and every example in examples/.
.section {
display: grid;
grid-template-columns: 200px minmax(0, 1fr);
column-gap: clamp(32px, 5vw, 64px);
}
@media (max-width: 720px) {
.section { grid-template-columns: 1fr; }
}
Composition Patterns
The dominant element
Every page has one element that dominates (see SKILL.md Step 5): usually the hero headline or the product visual. Compose around it:
- Give it the largest type or the largest box on the page.
- Everything else steps down deliberately — second level at ~60% of its size, third at ~35%.
- One full-bleed or oversized moment per page. Two is noise.
Reading paths
- Z-pattern for sparse, hero-led pages: strong top-left anchor, diagonal to a CTA bottom-right.
- F-pattern for text-heavy pages: reinforce with a strong left rule — meta column, numbered index, aligned labels.
- Single-axis scroll for editorial: one strong centerline, breaks only for full-bleed spreads.
Alternation
Down the page, alternate section structures — never repeat one module twice in a row:
hero (5/7 split, type-led)
→ statement (full-width, large type, no grid)
→ index (meta-column list)
→ detail (7/5 split, visual-led)
→ quote or manifesto (full-bleed or inset)
→ action (2-column, type + form)
→ footer (colophon)
If two consecutive sections have the same structure, flip the split or merge them.
Overlap and inset (use once)
An image bleeding out of its column by one gutter (margin-right: calc(-1 * var(--sp-5))), or a caption overlapping an image edge, adds craft. Once per page. More is decoration.
Responsive Strategy
Mobile-first, four breakpoints, tested at five widths.
| Breakpoint | Change what |
|---|---|
| Base (320–479px) | Single column, type scale steps down ~1 tier, meta-columns collapse above content |
min-width: 480px |
Two-column utility layouts (stats, small cards), larger touch paddings |
min-width: 768px |
Grid splits appear (5/7 etc.), side nav space, larger section rhythm |
min-width: 1024px |
Full 12-col grid, meta-column pattern, --sp-9+ section spacing |
Test widths: 320, 375, 768, 1280, 1600. (checklist.md tests 375/768/1280 — 320 catches overflow, 1600 catches lonely stretched content.)
Collapse rules
- Multi-column zones collapse column by column — meta columns collapse to a top row, not to a wall of centered text.
- Left-aligned stays left-aligned at every size. Centering is not a mobile strategy.
- Hide nothing essential on mobile. If a section must be cut, cut it at the brief level, not in CSS.
- Tables: allow horizontal scroll inside the table wrapper (
overflow-x: auto), never the page. - Fluid type via
clamp()means most text needs no breakpoint overrides — seetypography.md. Breakpoints are for structure, not font sizes.
/* Structure at breakpoints — not font sizes */
.hero { grid-template-columns: 1fr; }
@media (min-width: 768px) {
.hero { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
}
Whitespace Rules
- Whitespace is a feature, not leftovers (
SKILL.mdprinciple 4). If a section feels crowded, the fix is usually--sp-9, not a background tint. - Air follows importance. Hero > section intros > body > captions.
- Never fill space with decoration because it feels empty. Empty is the design.
- Dense is allowed — indices, tables, technical docs are dense on purpose (see
aesthetics.md§3, §6). Density then needs hairline structure and mono numbers to read as order, not crowding.
Layout Anti-Patterns
| ❌ Don't | ✅ Do |
|---|---|
| Centered everything, every section | One dominant left-aligned composition; center only short statements |
| Identical thirds repeated down the page | Asymmetric splits (5/7, 3/9), alternating structures |
max-width: none full-window text |
Container system with a measure for body copy |
One-off margins (17px, 23px, 40px) |
The spacing scale, as tokens |
| Dividers between every section | Whitespace between sections; hairlines inside data only |
| Hero with 200px padding and 36px headline | Big type or big visual or generous air — the hero must justify its space |
| Every section same structure, same rhythm | Alternate splits and densities; one full-bleed moment |
| Hiding whole sections on mobile | Simplify structure, keep the content |
Fixed pixel widths on grid children (width: 400px) |
minmax(0, 1fr) tracks and max-width in ch/% |
| Horizontal page scroll from a wide child | minmax(0, 1fr) tracks, overflow-x: auto on table wrappers, max-width: 100% on media |
| Breakpoints that only change font sizes | Breakpoints change structure; type is fluid via clamp() |
A Working CSS Setup
:root {
--container-main: 1240px;
--container-text: 720px;
--container-read: 68ch;
--pad-inline: clamp(20px, 4vw, 48px);
--sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
--sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;
--sp-9: 96px; --sp-10: 128px;
--section-gap: var(--sp-7); /* mobile */
}
@media (min-width: 768px) { :root { --section-gap: var(--sp-8); } }
@media (min-width: 1024px) { :root { --section-gap: var(--sp-9); } }
body { margin: 0; }
.container {
max-width: var(--container-main);
margin-inline: auto;
padding-inline: var(--pad-inline);
}
.container--text { max-width: var(--container-text); }
section { padding-block: var(--section-gap); }
.grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--sp-5); }
.split { display: grid; column-gap: clamp(32px, 5vw, 80px); }
.split--5-7 { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
.split--3-9 { grid-template-columns: minmax(0, 3fr) minmax(0, 9fr); }
.split--meta { grid-template-columns: 200px minmax(0, 1fr); column-gap: clamp(32px, 5vw, 64px); }
.measure { max-width: var(--container-read); }
@media (max-width: 767px) {
.split, .split--5-7, .split--3-9, .split--meta { grid-template-columns: 1fr; row-gap: var(--sp-6); }
}
*, *::before, *::after { box-sizing: border-box; }
img, svg, video { max-width: 100%; height: auto; }
Layout QA
- One container system; body copy capped at ~45ch inside grids
- All spacing comes from the scale — zero one-off values
- Hero is asymmetric or has a deliberate typographic moment
- No two consecutive sections share a structure
- One full-bleed moment maximum
- Tested at 320, 375, 768, 1280, 1600 — no horizontal scroll at any width
- Breakpoints change structure, not font sizes
- Left alignment preserved at every size
See also: typography.md (fluid type), color.md (surface rhythm between sections), anti-patterns.md §6, §11, §12 (structural slop), checklist.md (Layout section).