Skip to content

02 β€” FOUNDATIONS

Rhythm

Six numbers govern every vertical gap in the system: three between siblings, three around a section. Nothing sits between them, and you never write a margin.

THESE WERE MEASURED, NOT CHOSEN

The source design puts 44px after a section head twenty times, and 56px before a larger break seventeen more. Its section padding falls into three clusters and no others. That is a rhythm rather than a designer's mood, which is what makes it transferable β€” so the system inherited the numbers instead of picking new ones.

BETWEEN SIBLINGS3 STEPS
--ds-flow-tight24px

Blocks that belong to each other β€” a caption under its table, a note under a form.

--ds-flowclamp(32px, 4cqi, 44px)

The default. A section head to its content, and most gaps inside a section.

--ds-flow-looseclamp(40px, 5cqi, 56px)

A larger break: two distinct ideas inside one section.

Applied by the flow primitive, which is a flex column with a gap. Every gap is fluid except the tight one β€” shrink a twenty-four-pixel gap and it stops reading as a gap at all.

<section class="shell shell-section-lg flow">
  <div class="section-head">…</div>
  <div class="hairline-grid">…</div>
</section>
AROUND A SECTION3 STEPS
--ds-pad-bandclamp(40px, 5%, 72px)

A band holding one module. Tighter than a section of prose.

--ds-pad-sectionclamp(56px, 7vw, 96px)

An ordinary section.

--ds-pad-section-lgclamp(64px, 8vw, 120px)

A major section β€” the ones with a number in their head.

Reached as .shell-band, .shell-section and .shell-section-lg. These use vw rather than cqi, and that is the one place it is correct: a section's own breathing room is a property of the page, and the shell is the page.

THE RHYTHM IS A THEME SETTING

All six live in the theme file beside the colours, so a theme owns the spacing of a site as much as its palette. A denser product UI and an airy marketing page can be the same library with two themes β€” and it works on a subtree, like everything else.

SHIPPED
a block
another
DENSER
one variable changed
another

BOTH BLOCKS CARRY IDENTICAL CLASSES β€” tools/check-theme.mjs reads the computed gap off each and fails if they match.

NO BASELINE GRID, AND WHY

A true baseline grid snaps every line of text to a fixed lattice. It is beautiful in print and it does not survive here: the type scale is fluid, two families with different metrics share the page, and a component may sit in any width of container. A lattice tuned to one of those is wrong in the other three, and enforcing it would mean freezing the type β€” which is a much larger loss than a few pixels of drift.

What survives is a step scale: a small set of gaps, used consistently, with nothing between them. That is what these six numbers are.

THE RULE, AND ITS TEETH

A page never writes a vertical margin. Before that rule had a home, one page built on this system grew five spellings of one gap β€” 24, 32, 36, 44 and 56 pixels β€” each a reasonable guess at the moment it was made. That is what a system without a named rhythm produces however careful the author is.

The library cannot police what a site writes in a style attribute, so this rule grows its teeth in the consuming project: a check there refuses an inline margin-top outright. A one-off gap sets --flow on the container instead, which is the only sanctioned override.

<!-- a one-off, on the CONTAINER β€” never a margin on a child -->
<div class="flow" style="--flow: var(--ds-flow-loose)">