Page Grid
The horizontal frame around every Brik page — the width-container recipe, the canonical page-inset token, the one content-width ladder that caps both bands and text columns, and the rule that a column has one width and one left edge.
Content Rhythm governs the vertical axis; this page governs the horizontal one. Every content band on a Brik page — a marketing section's container, a header, a footer, a consumer site's nav — is framed the same way: a --content-width-* band, centred, inset from the viewport edge by the canonical page inset. One recipe, so BDS sections and consumer containers stay flush-aligned at every breakpoint (ADR-025).
The width-container recipe
Three declarations, always together:
.my-container {
max-width: var(--bds-content-width-xl); /* the band */
margin-inline: auto; /* centred */
padding-inline: var(--bds-page-inset); /* the page-edge inset */
}--content-width-xl (1280px) is the default band — the value the section shell, SiteHeader, Footer, and the brikdesigns nav all resolve to. Narrower bands are a per-role choice from the width roles table, not a new number.
The lint-page-grid gate fails any container rule whose padding-inline bypasses --page-inset — a container on an ad-hoc inset drifts out of alignment with every other band on the page.
Page inset vs column gutter
"Gutter" names two different relationships. Only the page edge gets a dedicated token; space between columns is already governed by the component rhythm map.
| Sense | Relationship | Mechanism |
|---|---|---|
| Page inset | Viewport edge ↔ content band | padding-inline: var(--bds-page-inset) on the container |
| Column gutter | Sibling ↔ sibling in a Grid or Cluster | The primitive's gap, from the --gap-* scale |
This is the gap-vs-padding rule from Content Rhythm applied horizontally: gap is between siblings, padding is the edge.
--page-inset (renamed from --gutter-page; the old name survives as a deprecated alias) is part of the layout tier: it scales with the viewport — 16px at 320, 32px at 768, 96px at 1440 and above — and does not change with spacing modes (ADR-042; its old density ladder is gone). The fluid value lives only on this one BDS token. Never re-declare it with a site-level clamp(): brikdesigns once did, and its nav drifted from its sections because two tokens disagreed — one shared token can't. In TS, import pageInset from @bds-tokens rather than writing the var() string inline.
Content width roles
One ladder caps everything horizontal, and each rung carries exactly one role — stated in its name. A -text- rung caps a text column; a bare rung sizes a band (ADR-041).
| Token | Width | Caps | Role |
|---|---|---|---|
--bds-content-width-text-xs | 448px | text | Short intro — a single-line eyebrow + title |
--bds-content-width-text-sm | 600px | text | Section intro — title + description (the default) |
--bds-content-width-text-md | 720px | text | Long-form prose, rich-text body |
--bds-content-width-sm | 640px | band | Narrow band, focused forms |
--bds-content-width-md | 800px | band | Standard text-led body sections |
--bds-content-width-lg | 1024px | band | Feature grids, CTA bands |
--bds-content-width-xl | 1280px | band | The page band — hero, header, footer, most sections |
--bds-content-width-full | 100% | band | Full-bleed; prefer an explicit width |
The text rungs interleave numerically with the band rungs (600 < 640 < 720 < 800). That is expected and harmless: pick a rung by its role, which the name states — never by sorting the ladder.
There was a second --measure-* family in ch for the text column; ADR-041 retired it into the -text- rungs above, at the px widths it already rendered (the old names ship one more minor as deprecated aliases, then drop). Nothing in BDS scales body type (--body-md is a fixed 16px, only --display-fluid-* clamps, and [data-mode-spacing] never touches font-size), so ch never tracked anything — while two siblings sharing one ch cap at different type sizes resolved to two different pixel widths. Widths are static across spacing modes either way: a readable line length is a typographic constant, not a density knob.
These are content constraints, not breakpoints — even where the pixel values coincide. For media queries use the mediaQueries export from @bds-tokens (or breakpoints for a raw width); CSS custom properties cannot appear inside @media conditions, so the --breakpoint-* CSS variables are reference-only.
There is no responsive-spacing axis: density modes are the only spacing modulation in BDS. Do not introduce breakpoint-varying gutters or gaps.
The column rule
A column has one content width and one left edge. The element that OWNS the column sets the width; blocks nested inside it never re-cap themselves. Two caps in one column is the defect, not the fix.
This is the rule most often missed. A section's content container picks one rung and every block inside it inherits that column — a nested prose block, a hero block, a media block do not each declare their own max-width. Two caps in one column give the eye two different left edges and read as broken alignment, whichever tokens they use.
/* ✅ one owner, one cap */
.split__content { max-width: var(--bds-content-width-text-sm); }
.split__content > .prose { /* no max-width — inherits the column */ }
/* ❌ two caps, two left edges */
.split__content { max-width: var(--bds-content-width-text-sm); }
.split__content > .hero { max-width: var(--bds-content-width-sm); }
.split__content > .prose { max-width: var(--bds-content-width-text-md); }If a block genuinely needs a different width from its siblings, it is not in that column — give it its own column, or restructure. Don't re-cap inside one.
Text column vs band
A band width is not a text width. --content-width-xl (1280px) sizes the band; a title or description spanning the full 1280px is unreadable. The text column is the readable column inside the band — a narrower cap applied to the text group, not to the container.
Same ladder, two jobs — keep the jobs separate:
| Sizes | Rung | Mechanism | |
|---|---|---|---|
| Band | The container / section width | --content-width-sm…full | max-width + margin-inline: auto on the container |
| Text column | The text column inside it | --content-width-text-* | max-width on the text group — usually via SectionHeader |
SectionHeader
Don't hand-roll the cap. A centered section intro — eyebrow / title / description above a card grid, a CTA band, a hero — is a SectionHeader: it composes ContentBlock for the slot rhythm (ADR-023) and adds only the measure + centering — the concern ContentBlock deliberately does not own.
<SectionHeader
title="What we do"
description="From branding to websites to behind-the-scenes systems…"
/>
// centered · measure-md (--bds-content-width-text-sm, 600px) · <h2> title at heading-lg — nothing to overridemeasure (sm / md / lg, default md) picks the cap; align (center / start, default center) picks whether the column centers in the band or anchors to its start. SectionHeader never sets a band width — drop it inside the section's width-container and it caps the text within. This is the single replacement for the ad-hoc measures a section header used to be written with (600px, 700px, width: 70%, max-width: none, a reused band rung).
A text column is not a band. Never cap a section intro with a band rung (--content-width-sm…full — those size the container) or with a raw px / percentage. Reach for SectionHeader — or a --content-width-text-* rung directly only when hand-rolling a text group SectionHeader doesn't cover. Reading a band rung's role as "prose" is what shipped brikdesigns#1827; ADR-041 put the role in the name so the table can only be read one way.
Where the recipe is set
Like the rhythm scale, you rarely write the recipe by hand — it is baked into the layer that owns each surface.
- Marketing sections →
.bds-blueprint-section__container(ADR-021). Override band or inset via the section's hooks, never by hand-rolling a sibling container:
.bds-cta__container {
--bds-blueprint-section-content-width: var(--bds-content-width-lg);
--bds-blueprint-section-padding-inline: var(--bds-padding-xl);
}- Product apps → the
Pageprimitive; itspaddingprop sets the shell inset (lgis the page gutter). - Full-bleed backgrounds with constrained content →
Footer'scontainerMaxWidthshape: the background spans the viewport whilepadding-inline: max(var(--page-inset), calc((100% - var(--content-width-xl)) / 2))centres the content band. - Section vertical band →
padding-block: var(--section-padding-block)— 64px at 320, 80px at 768, 104px at 1440 and above (ADR-042; it replaces ADR-021's7vwclamp). A page is a stack of full-bleed sections; the vertical band is--section-padding-block, the horizontal band is this recipe.
Related
- Content Rhythm — the vertical axis this page mirrors.
- Page Structure — the HTML skeleton these containers live in.
- Composition Layers — the Blueprint → Layout → Container → Block or Control → Component model.
- Spacing — the
--padding-*scale, the density modes that modulate it, and the layout tier that sets the page inset. - Size — the
--content-width-*ladder's token catalog.
Banner Groups
The order, placement, and spacing rules for a surface carrying more than one banner — and the cases where the second banner is the wrong answer.
Image Sourcing
Where imagery comes from on a Brik web build — the photography-first sourcing ladder, the AI-imagery policy, and how to wire the asset into Image and Frame.