layout-grid — rhythm and asymmetry over wallpaper
Stage: Phase 3 — Foundation - Reads: design/DIRECTION.md, design/BRIEF.md - Writes: design/SYSTEM.md §layout + container tokens handed to ultraweb:tokens
Standard
Layout is where decisions become visible. First-grade means: ONE container system (max 4 width tiers), a 12-column vocabulary with named splits, section spacing that changes tempo down the page, and at least one recurring asymmetry pattern — taste mandates it. Scrolling the page must feel like compression and release, not a metronome. The bar, concretely:
- One default content width; body copy never exceeds 72ch.
- ≥1 asymmetric split per page; 6/6 is the exception, reserved for true equals.
- ≥2 section-padding sizes per page; no three consecutive sections share a size.
- Everything lands on the 4px base unit; asymmetry is aligned to the grid, never off-grid drift.
Process
- Read DIRECTION.md for density stance (editorial-airy vs product-dense) and where the signature move lives — it usually claims the boldest asymmetry.
- Set the container strategy (below); record the three tier values.
- Fix the grid vocabulary: base columns, gaps, and 2-3 named splits this site will reuse.
- Build the rhythm map: assign every SITEMAP section a size from compressed/standard/release.
- Choose 1-2 asymmetry patterns from the named set as the site's recurring moves. One pattern repeated is a voice; a new pattern every section is chaos.
- If the wireframe calls for a bento, apply the composition rules below.
- Write SYSTEM.md §layout: tiers, splits, rhythm map, chosen patterns, bento verdict. Hand
--container-* values to ultraweb:tokens.
Container strategy
@theme {
--container-prose: 65ch; /* long-form text */
--container-content: 72rem; /* default sections; 80rem for marketing-heavy briefs */
--container-wide: 88rem; /* bento, galleries, data tables */
}
- Usage:
max-w-content mx-auto px-4 sm:px-6 lg:px-8 — --container-* tokens drive max-w-* in Tailwind v4.
- Full-bleed is a deliberate breakout (Edge Bleed below), never a missing
max-w.
- Four tiers total (prose/content/wide/bleed). A fifth width appearing in code means the system leaked.
Grid vocabulary
- Base:
grid grid-cols-12, gap-4 (16px) mobile, gap-6–gap-8 (24-32px) desktop.
- Named splits: Lead 7/5 (text-led feature), Heavy 8/4 (media-led), Reverse 5/7 (alternating sections), Margin Note 3/9 (editorial meta rail). 6/6 only for comparisons and before/afters.
- Sub-layouts inside grid cells respond via container queries (
@container on the cell, @sm: inside — Tailwind v4 core, no plugin).
- Dev grid overlay — confirm alignment against visible guides during the build, not retroactively at gate time: a dev-only
<GridOverlay /> (rendered only when process.env.NODE_ENV === 'development', toggled by a keyboard shortcut) paints the 12 columns as semi-transparent stripes plus a fixed readout of the active breakpoint, gap, and container width. Pure CSS, zero deps, tree-shaken from production.
Vertical rhythm — compression and release
Uniform py-24 on every section is wallpaper rhythm, banned by taste. Three named sizes:
| Size |
Classes |
Use |
| compressed |
py-12 md:py-16 (48/64px) |
logo strips, stat bars, tickers, dense proof |
| standard |
py-20 md:py-28 (80/112px) |
default content sections |
| release |
py-28 md:py-40 (112/160px) |
hero exit, pre-CTA, chapter breaks |
Rules:
- The page arcs toward the primary CTA: compress the proof, release before the ask.
- Related sections compress toward each other; topic changes get release. Space encodes grouping.
- Adjacent sections sharing a background are one surface — split the boundary (
pb-10 + pt-10), never stack two full paddings.
- Space within a section < space between sections, always. Heading-to-content:
mt-12–mt-16 (48-64px).
Offscreen wrappers — any section past the third fold (FAQ, blog/case archives, footer sitemap) wraps in content-visibility: auto with a measured contain-intrinsic-size (e.g. auto 900px): the browser skips style/layout/paint for offscreen DOM entirely — native virtualization for static content, strictly stronger than image lazy-load. Never ship a bare auto size — an unmeasured height trades paint cost for CLS. Exempt the LCP-holding section, sticky-scroll sequences, and the showpiece; ultraweb:gate-performance verifies the property and a zero CLS delta.
Asymmetry patterns (named)
- Offset Split — Lead 7/5 or Heavy 8/4 with the two blocks' top edges deliberately misaligned by 2-4rem (
mt-8–mt-16 on one side). The workhorse; safe everywhere.
- Edge Bleed — text stays in the container, media escapes to the viewport edge on ONE side:
lg:mr-[calc(50%-50vw)] on the media column. Best for product shots and photography.
- Overlap Stack — a card or stat block crosses a section boundary with
-mt-16–-mt-24 (64-96px), z-indexed above a contrasting surface. Stitches hero to first section; one per page.
- Staggered Rail — a 2-column item grid where column two is pushed down
mt-12–mt-24 (48-96px). Breaks card-grid monotony; suits portfolios and testimonials.
- Margin Note — 3/9 split: narrow sticky rail (section number, eyebrow, TOC) beside wide content. Editorial directions; pairs with long-form.
- Editorial Collage — 2-3
next/image in a shared relative wrapper, each rotated 2-6deg one consistent direction and overlapping 10-20%, separated by depth's shadow tokens; one image bleeds past max-w via negative margin. Deliberate print-spread layering — never a wallpaper photo grid. Max one per page, never on the section carrying the signature move. Below 768px: rotation off, stacked full-width — a decided fallback, not inherited.
Bento composition
- 4-7 cells on
grid-cols-2 md:grid-cols-4 (or 6); exactly ONE hero cell ≥2× any other's area (col-span-2 row-span-2).
- ≤3 distinct cell sizes; ONE gap value site-wide (
gap-4 or gap-6); one radius token from shape-language.
- Every cell earns real content — a stat, a live mini-demo, an image. No filler tiles, no "and much more".
- Max 1 media-heavy or animated cell; the rest stay typographically quiet so the hero cell sings.
- Mobile collapse: source order = importance order; the hero cell renders first at 375px.
Responsiveness & a11y
- DOM order = reading order. Build asymmetry with grid placement, not
order-* that splits tab order from visual order (WCAG 2.4.3).
- Overlap Stack: verify the overlapping element covers no interactive targets underneath; targets stay ≥44px even in compressed sections.
- Every pattern needs a decided 375px stacking order — never let it fall as it may. Check in gate-responsive screenshots.
Anti-patterns
py-24 on most sections — grep py-24; >60% of sections = wallpaper rhythm.
max-w-7xl mx-auto + text-center repeated per section — the centered-column wallpaper taste bans.
grid-cols-3 gap-6 identical cards as the features section — the three-card slop; defer to feature-sections.
grid-cols-2 at 6/6 for every split — no hierarchy between the halves.
- Random negative margins to fake asymmetry — off-grid misalignment is a defect, not composition.
- Bento with all-equal cells, or a filler cell padding out the grid.
- Ad-hoc widths — grep
max-w-[; arbitrary container values mean the tier system is being bypassed.
Worked example — Studio Norra, Oslo agency portfolio layout
Moved to references/example.md — read only when this build's case is genuinely ambiguous; the sections above are the decision material.
Composes with
Moved to references/composes.md — the handoff map; load it when orchestrating this skill against its neighbors.
1---2name: layout-grid3description: Design the page layout system for an ultraweb build — container width strategy, 12-column and asymmetric grid vocabulary, section vertical rhythm with deliberate compression and release (never uniform py-24), at least four named asymmetry patterns, and bento composition rules — written to design/SYSTEM.md §layout during the foundation phase. Invoke in Phase 3 of the ultraweb pipeline once DIRECTION.md exists, or whenever layout feels monotonous, every section is the same centered column, spacing rhythm is flat, or someone asks for "a bento grid", "an offset layout", "asymmetric sections", "better section spacing", or "container widths".4---56# layout-grid — rhythm and asymmetry over wallpaper78**Stage:** Phase 3 — Foundation - **Reads:** design/DIRECTION.md, design/BRIEF.md - **Writes:** design/SYSTEM.md §layout + container tokens handed to ultraweb:tokens910## Standard1112Layout is where decisions become visible. First-grade means: ONE container system (max 4 width tiers), a 12-column vocabulary with named splits, section spacing that changes tempo down the page, and at least one recurring asymmetry pattern — taste mandates it. Scrolling the page must feel like compression and release, not a metronome. The bar, concretely:1314- One default content width; body copy never exceeds 72ch.15- ≥1 asymmetric split per page; 6/6 is the exception, reserved for true equals.16- ≥2 section-padding sizes per page; no three consecutive sections share a size.17- Everything lands on the 4px base unit; asymmetry is aligned to the grid, never off-grid drift.1819## Process20211. Read DIRECTION.md for density stance (editorial-airy vs product-dense) and where the signature move lives — it usually claims the boldest asymmetry.222. Set the container strategy (below); record the three tier values.233. Fix the grid vocabulary: base columns, gaps, and 2-3 named splits this site will reuse.244. Build the rhythm map: assign every SITEMAP section a size from compressed/standard/release.255. Choose 1-2 asymmetry patterns from the named set as the site's recurring moves. One pattern repeated is a voice; a new pattern every section is chaos.266. If the wireframe calls for a bento, apply the composition rules below.277. Write SYSTEM.md §layout: tiers, splits, rhythm map, chosen patterns, bento verdict. Hand `--container-*` values to ultraweb:tokens.2829## Container strategy3031```css32@theme {33 --container-prose: 65ch; /* long-form text */34 --container-content: 72rem; /* default sections; 80rem for marketing-heavy briefs */35 --container-wide: 88rem; /* bento, galleries, data tables */36}37```3839- Usage: `max-w-content mx-auto px-4 sm:px-6 lg:px-8` — `--container-*` tokens drive `max-w-*` in Tailwind v4.40- Full-bleed is a deliberate breakout (Edge Bleed below), never a missing `max-w`.41- Four tiers total (prose/content/wide/bleed). A fifth width appearing in code means the system leaked.4243## Grid vocabulary4445- Base: `grid grid-cols-12`, `gap-4` (16px) mobile, `gap-6`–`gap-8` (24-32px) desktop.46- Named splits: **Lead 7/5** (text-led feature), **Heavy 8/4** (media-led), **Reverse 5/7** (alternating sections), **Margin Note 3/9** (editorial meta rail). 6/6 only for comparisons and before/afters.47- Sub-layouts inside grid cells respond via container queries (`@container` on the cell, `@sm:` inside — Tailwind v4 core, no plugin).48- **Dev grid overlay** — confirm alignment against visible guides during the build, not retroactively at gate time: a dev-only `<GridOverlay />` (rendered only when `process.env.NODE_ENV === 'development'`, toggled by a keyboard shortcut) paints the 12 columns as semi-transparent stripes plus a fixed readout of the active breakpoint, gap, and container width. Pure CSS, zero deps, tree-shaken from production.4950## Vertical rhythm — compression and release5152Uniform `py-24` on every section is wallpaper rhythm, banned by taste. Three named sizes:5354| Size | Classes | Use |55|---|---|---|56| compressed | `py-12 md:py-16` (48/64px) | logo strips, stat bars, tickers, dense proof |57| standard | `py-20 md:py-28` (80/112px) | default content sections |58| release | `py-28 md:py-40` (112/160px) | hero exit, pre-CTA, chapter breaks |5960Rules:61- The page arcs toward the primary CTA: compress the proof, release before the ask.62- Related sections compress toward each other; topic changes get release. Space encodes grouping.63- Adjacent sections sharing a background are one surface — split the boundary (`pb-10` + `pt-10`), never stack two full paddings.64- Space within a section < space between sections, always. Heading-to-content: `mt-12`–`mt-16` (48-64px).6566**Offscreen wrappers** — any section past the third fold (FAQ, blog/case archives, footer sitemap) wraps in `content-visibility: auto` with a *measured* `contain-intrinsic-size` (e.g. `auto 900px`): the browser skips style/layout/paint for offscreen DOM entirely — native virtualization for static content, strictly stronger than image lazy-load. Never ship a bare `auto` size — an unmeasured height trades paint cost for CLS. Exempt the LCP-holding section, sticky-scroll sequences, and the showpiece; ultraweb:gate-performance verifies the property and a zero CLS delta.6768## Asymmetry patterns (named)69701. **Offset Split** — Lead 7/5 or Heavy 8/4 with the two blocks' top edges deliberately misaligned by 2-4rem (`mt-8`–`mt-16` on one side). The workhorse; safe everywhere.712. **Edge Bleed** — text stays in the container, media escapes to the viewport edge on ONE side: `lg:mr-[calc(50%-50vw)]` on the media column. Best for product shots and photography.723. **Overlap Stack** — a card or stat block crosses a section boundary with `-mt-16`–`-mt-24` (64-96px), z-indexed above a contrasting surface. Stitches hero to first section; one per page.734. **Staggered Rail** — a 2-column item grid where column two is pushed down `mt-12`–`mt-24` (48-96px). Breaks card-grid monotony; suits portfolios and testimonials.745. **Margin Note** — 3/9 split: narrow sticky rail (section number, eyebrow, TOC) beside wide content. Editorial directions; pairs with long-form.756. **Editorial Collage** — 2-3 `next/image` in a shared `relative` wrapper, each rotated 2-6deg one consistent direction and overlapping 10-20%, separated by depth's shadow tokens; one image bleeds past `max-w` via negative margin. Deliberate print-spread layering — never a wallpaper photo grid. Max one per page, never on the section carrying the signature move. Below 768px: rotation off, stacked full-width — a decided fallback, not inherited.7677## Bento composition7879- 4-7 cells on `grid-cols-2 md:grid-cols-4` (or 6); exactly ONE hero cell ≥2× any other's area (`col-span-2 row-span-2`).80- ≤3 distinct cell sizes; ONE gap value site-wide (`gap-4` or `gap-6`); one radius token from shape-language.81- Every cell earns real content — a stat, a live mini-demo, an image. No filler tiles, no "and much more".82- Max 1 media-heavy or animated cell; the rest stay typographically quiet so the hero cell sings.83- Mobile collapse: source order = importance order; the hero cell renders first at 375px.8485## Responsiveness & a11y8687- DOM order = reading order. Build asymmetry with grid placement, not `order-*` that splits tab order from visual order (WCAG 2.4.3).88- Overlap Stack: verify the overlapping element covers no interactive targets underneath; targets stay ≥44px even in compressed sections.89- Every pattern needs a decided 375px stacking order — never let it fall as it may. Check in gate-responsive screenshots.9091## Anti-patterns9293- `py-24` on most sections — grep `py-24`; >60% of sections = wallpaper rhythm.94- `max-w-7xl mx-auto` + `text-center` repeated per section — the centered-column wallpaper taste bans.95- `grid-cols-3 gap-6` identical cards as the features section — the three-card slop; defer to feature-sections.96- `grid-cols-2` at 6/6 for every split — no hierarchy between the halves.97- Random negative margins to fake asymmetry — off-grid misalignment is a defect, not composition.98- Bento with all-equal cells, or a filler cell padding out the grid.99- Ad-hoc widths — grep `max-w-[`; arbitrary container values mean the tier system is being bypassed.100101## Worked example — Studio Norra, Oslo agency portfolio layout102103Moved to `references/example.md` — read only when this build's case is genuinely ambiguous; the sections above are the decision material.104105## Composes with106107Moved to `references/composes.md` — the handoff map; load it when orchestrating this skill against its neighbors.