Spacing

Two scales, because space inside a thing and space between things are not the same decision — one should hold still, the other should breathe.

A button's padding at 8px should be 8px on a phone and on a 4K monitor — it is a property of the button, and a button does not get roomier because the window did. The gap between two bands is the opposite: it is a property of the page, and a page with the same gutters at 375px and 2560px is cramped at one end and empty at the other.

So component is fixed px and layout is fluid. Resize this window and watch the two tables below — the layout bars move, the component bars do not.

Component

Fixed px on the same 4px grid as element sizes — deliberately unmoving.

TokenValuePreview
--space-component-0…
--space-component-0-25…
--space-component-0-5…
--space-component-1…
--space-component-2…
--space-component-3…
--space-component-4…
--space-component-5…
--space-component-6…
--space-component-8…
--space-component-10…
--space-component-12…
--space-component-16…

base × N on a 4px base, the same grid --size-element-* sits on — so a 24px control and a 24px gap beside it are the same number rather than two that happen to agree.

Dense at the bottom and thinning above 24px, because that is where the decisions are: 4px against 8px of padding changes how a control feels, 48px against 52px does not.

Layout

The other half of the split — these breathe with the container.

TokenValuePreview
--space-layout-0…
--space-layout-0-25…
--space-layout-0-5…
--space-layout-1…
--space-layout-1-5…
--space-layout-2…
--space-layout-3…
--space-layout-4…
--space-layout-6…
--space-layout-8…

Every step is one clamp() holding three numbers: a floor so a phone still has edges, a rate that grows with the container, and a ceiling so an ultrawide monitor does not tear the page apart.

The unit is cqi, not vw, and that is the decision that makes the rest of the system work. cqi measures the container, and the Section is the only query container dede has — so a band rebases its rhythm to the space it was actually given rather than to the window. A Section inside a narrow column spaces itself like a narrow column. With vw, the shell's 15rem nav would have every band on every page spacing itself as though that nav were not there.

One consequence worth holding onto: cqi is a width measure applied to both axes. A vertical gap and a horizontal gap of the same step are the same number, because width drives height. That is what keeps a page's rhythm square as it scales, and it is why band padding lives on the Stack rather than the Section — both breathing axes have to measure the same thing.

Why the floor is 12N

A phone needs about 24px of edge breathing, and the obvious way to buy it is to reach for a bigger step. That does not work, and the reason is worth stating: a step scales the floor, the rate and the ceiling together, so taking a bigger step on mobile takes its desktop value with it. More on mobile, unchanged on desktop is unsayable in step numbers.

Raising the floor says it directly, and changes nothing at any width where the rate wins — which is every container over 600px. When a value needs to change at one size only, move the term that governs that size, not the step.

N then cancels out of both thresholds, so one pair of widths describes the entire scale: the floor holds below 600px, the rate governs from 600 to 1200, and the ceiling holds above 1200. Every step crosses at the same two widths, which is what stops a page changing character step by step as it grows. The floor is exactly half the ceiling.

--space-layout-1-5 is the one step with no consumer. It completes the second doubling chain — 1-5 → 3 → 6 alongside 0-25 → 0-5 → 1 → 2 → 4 → 8 — and stays on the same terms as the unreached breakpoints: unused is fine, undeliverable is not.