Foundation · stable · v1.0.0 · owner: Site system lead · contract 19 of 20 points (open: figma)
Reference
Source reference; not an executed check.
--space-0- space.scale.0
--space-1- space.scale.1
--space-2- space.scale.2
--space-3- space.scale.3
--space-4- space.scale.4
--space-5- space.scale.5
--space-6- space.scale.6
--space-7- space.scale.7
--space-8- space.scale.8
--space-9- space.scale.9
--space-10- space.scale.10
--space-11- space.scale.11
--space-12- space.scale.12
--space-13- space.scale.13
Do and don't
2 positive / 2 negative authored documentation examples (2 each required); 1 reference (1 required); 0 failed. References and failures do not count; this is not behavior acceptance.
Do: The section owns one padding
Authored by spacing.tsRendered consumer example; not an executed correctness check. titled-section Isolated document follows the browser's colour and contrast preferences. The outer theme toolbar does not control this document.
Open example: Spacing and rhythm · Do: The section owns one padding Use the full-page example to follow page links.
Do: Use the recorded scale for a real gap
Authored by spacing.tsRule illustration; not an executed check. spacing / spacing.scale
A component declares its content gap from an existing --space-* semantic binding, such as var(--space-4).
The gap changes in the system rather than becoming a per-page pixel nudge.Don't: Empty paragraphs create section separation
Authored by spacing.tsRule illustration; not an executed check. spacing / spacing.one-padding
<p>The argument ends.</p><p> </p><p> </p><h2>The next section</h2>
Blank content substitutes for the owning template's spacing.Don't: A one-off gap replaces the scale
Authored by spacing.tsRule illustration; not an executed check. spacing / spacing.scale
.local-heading-group { gap: 10px; }
The consumer inserts a raw off-scale value instead of its existing component binding.Rules
| Rule | Why | Check |
|---|---|---|
| Spacing uses the scale tokens only. | Consistent rhythm; the brand gate checks it. | Automated: scripts/site/check-brand.ts |
| Each template has one padding, declared in its contract. | Templates that each chose their own padding made the page lurch (D-SITE-005). | Automated: scripts/site/system/check.ts (spacing point) |
API
No props.
Contract details
The full contract: identity, anatomy, variants, states, tokens, layout, motion, accessibility, content, tests
Identity
| Aspect | Value |
|---|---|
| Id | spacing |
| Layer | Foundations |
| Category | foundation |
| Owner | Site system lead |
| Status | stable |
| Version | 1.0.0 |
| Source | apps/showcase/site/system/foundations/spacing.ts (private repository; the contract and the render function are this one module) |
| Version | Date | Change |
|---|---|---|
| 1.0.0 | 2026-10-26 | Spacing and rhythm as one module: its markup moved out of the page builder (scripts/site) into the system. |
Use when
- Every gap, padding and margin. Why: A fixed scale makes the rhythm consistent across pages.
Do not use when
- Pixel nudges. Why: An off-scale value is a one-off nobody can maintain.
Anatomy
| Part | What it is |
|---|---|
| scale | --space-0 … --space-13 |
| section | --section: clamp(64 px, 10vw, 160 px) |
| rhythm | One padding per template (D-SITE-005) |
Made of: nothing (a base element).
Used in: library-overview.
Variants
| Variant | When | Why |
|---|---|---|
| light and dark | Every page, following the reader's setting. | The same roles resolve to light or dark values, so no element chooses its own. |
States
| State | Rendered or not applicable |
|---|---|
| default | The specimen. |
| hover | Not applicable: A foundation is not interactive. |
| focus-visible | Not applicable: Not focusable. |
| active | Not applicable: Not interactive. |
| disabled | Not applicable: Nothing to disable. |
| loading | Not applicable: Values are built into the stylesheet. |
| empty | Not applicable: The build fails if a value is missing. |
| error | Not applicable: Unresolved values fail the brand gate, not the reader. |
| current | Not applicable: No current item. |
| visited | Not applicable: Not a link. |
| expanded | Not applicable: Does not open. |
Tokens
| Part | Token |
|---|---|
| --space-* | The scale |
| --section | Section padding |
| --section-sm | Smaller section padding |
Layout
| Aspect | Value |
|---|---|
| breakpoints | Required review at 390, 768, 1024 and 1440 px; execution is not implied by this declaration; columns stack under 768 px; the wrap keeps a 16 px gutter on phones. |
| wrapping | Text wraps; nothing scrolls sideways. |
| textExpansion | Japanese runs 20 to 40% longer; headings break between phrases (jaBreaks) and blocks grow downwards. |
Spacing and rhythm
| Aspect | Value |
|---|---|
| internal | Not applicable: a foundation has no box of its own. |
| external | Not applicable. |
| rhythm | Applies to every element that reads it. |
Motion
| Aspect | Value |
|---|---|
| None | A foundation does not move. |
Runtime and reuse
Render-only element; mount any registered child enhancements separately. Preserve required Site CSS, fonts, responsive and locale rules from nativeElement.
Accessibility passport
| Aspect | Value |
|---|---|
| role | Not rendered on the site; its specimen in the Library is a list |
| name | The foundation's name |
| keyboard | Not interactive. |
| focus | Not focusable. |
| screenReader | The specimen is a plain list of names and values. |
| contrast | Every text and ground pair in the palette is checked (check-brand). |
| target | Not a target. |
Content
| Slot | Length | Voice |
|---|---|---|
| heading, body and action groups (consumer-owned) | The consuming element's content budget, not a fixed-height text box | Keep a heading closer to its following text than the preceding section. Use the declared rhythm around meaningful groups rather than blank paragraphs or repeated spaces. |
| spacing token/value rows (fixed Library specimen) | Every SPACE entry with its --space-* index; no editable prop | Show the native scale exactly. Token indices and values are technical reference content, not new spacing choices for one page. |
Good: “One --section padding per template (spacing.one-padding / D-SITE-005)” · “An h2 with more space above than below (BRAND.md)”
Bad: “Empty paragraphs inserted to push the next heading down” · “A 10 px one-off gap between a heading and its copy (BRAND.md rejects off-scale spacing)”
Japanese: Japanese text retains the same spacing scale and section rhythm while its actual lines grow. Do not add spaces between Japanese characters or empty paragraphs to copy an English line height. Token identifiers stay unchanged; actual Japanese reflow and native review remain separate checks.
Icons and illustrations
None: this element uses no icon or illustration of its own.
Decisions and exceptions
Usage
23 owned-class occurrences in built pages plus metadata-token references in built CSS (measured by npm run site:system).
No application mechanism is declared.
Current check result: not assessed by this report. Declarations and source-file presence do not establish executed enforcement or accepted coverage.
Tests
| Aspect | Value |
|---|---|
| unit | tests/unit/site-system.test.ts: renders every do and don't example through the registry and checks the classes it owns |
| visual | Not yet: there are no screenshot baselines; the Library page is checked for sideways scroll at 1440 px by tests/site/system.spec.ts |
| a11y | tests/site/system.spec.ts: axe (0 serious or critical) on the element's Library page in light and dark |
| parity | scripts/site/system/check.ts: Library examples are rendered by the same render function the site imports |
Figma
not-yet: The Loom Site Figma library is the last stage of the plan; this element is not mapped yet.