Status: Canonical (D1 v1.0)
Last updated: August 10, 2026
Audience: Design Systems, Product Design, Front-End Engineering
Taxonomy placement: D1. Space & Rhythm (part of the 7-dimensional CEM token framework)
Companion specs:
cem-colors.md) — color weight pairs with spacing rhythmcem-coupling.md) — normative for interactive operabilitycem-shape.md) — bend/inset harmony rulescem-layering.md) — prominent layers "earn" breathing roomcem-stroke.md) — boundaries, dividers, focus indicatorscem-voice-fonts-typography.md) — reading rhythm validationcem-timing.md) — rhythm perceptionAppendices
This document defines a consumer-semantic spacing and layout rhythm token set that:
margin: 16px).dense | normal | sparse) without conflating it with D2
operability.This document is D1 (Space & Rhythm). It intentionally does not define interactive minimums; it consumes D2 coupling minimums as hard constraints.
Developers should apply tokens like:
gap-related, gap-group, gap-section (relationship semantics)inset-control, inset-container (surface semantics)coupling-guard-min (interaction safety contract; defined in D2)…and not value-centric names like space-16 in component code.
A small set of distinct, repeatable space steps should cover most UI. Extended values exist but are intentionally "rare".
Spacing must preserve:
rem / em and allow platform scaling)We use three layers:
In CEM, spacing/rhythm is not "just layout." It is a primary subset of Dimensional Tokens (the physical layer: size, distance, bend, stroke, depth).
Use this legend-level dimensional taxonomy to keep token categories coherent:
D1. Space & Rhythm (this document)
D2. Coupling & Compactness (adjacent; constrains D1) — see cem-coupling.md
D2c. Controls (adjacent; visual geometry constrained by D2) — see cem-controls.md
D3. Shape & Bend — see cem-shape.md
D4. Layering — see cem-layering.md
D5. Stroke & Separation — see cem-stroke.md
--cem-stroke-none, --cem-stroke-hair, --cem-stroke-standard, --cem-stroke-strong)D6. Typography Thickness — see cem-voice-fonts-typography.md
D7. Time & Motion Timing — see cem-timing.md
These rules prevent spacing changes from degrading operability, accessibility, or visual hierarchy:
dense | normal | sparse may adjust gaps/insets/gutters (D1) selectively.When spacing occurs between two interactive affordances, layout must respect D2 guard:
/* If the gap separates adjacent interactive affordances: */
.cem-gap-interactive {
gap: max(var(--cem-gap-related), var(--cem-coupling-guard-min));
}
cem-shape.md §8.5 for detailed guidance on bend vs inset readability.cem-voice-fonts-typography.md for typography tokens.The spacing scale uses semantic size names rather than pixel values, making intent clear while preserving M3 rhythm (4dp increments).
Eight steps covering common UI needs:
| Token | Value | Description | tier |
|---|---|---|---|
--cem-dim-xx-small | 0.25rem | 4px — micro gaps, icon padding | required |
--cem-dim-x-small | 0.5rem | 8px — related item gaps, control insets | required |
--cem-dim-small | 0.75rem | 12px — group gaps, inline rhythm | required |
--cem-dim-medium | 1rem | 16px — block gaps, container insets | required |
--cem-dim-large | 1.5rem | 24px — section gaps, surface insets | required |
--cem-dim-x-large | 2rem | 32px — page gaps, wide gutters | required |
--cem-dim-xx-large | 4rem | 64px — extended layout, hero spacing | required |
--cem-dim-xxx-large | 8rem | 128px — maximum breathing room | required |
Cross-reference: These tokens are consumed by D3 Shape (cem-shape.md) for bend values:
--cem-bend-smooth uses --cem-dim-x-small (8px)--cem-bend-surface uses --cem-dim-small (12px)--cem-bend-modal uses --cem-dim-large + --cem-dim-xx-small (~28px)| Semantic token | Value | M3 dp equivalent | Typical use |
|---|---|---|---|
--cem-dim-xx-small | 0.25rem | 4dp | Micro gaps, tight padding |
--cem-dim-x-small | 0.5rem | 8dp | Related items, control insets |
--cem-dim-small | 0.75rem | 12dp | Group gaps, inline spacing |
--cem-dim-medium | 1rem | 16dp | Block gaps, container insets |
--cem-dim-large | 1.5rem | 24dp | Section gaps, surface padding |
--cem-dim-x-large | 2rem | 32dp | Page gaps, wide gutters |
--cem-dim-xx-large | 4rem | 64dp | Extended layout spacing |
--cem-dim-xxx-large | 8rem | 128dp | Maximum layout spacing |
The taxonomy is organized by what the space means to the user.
Use when arranging siblings.
| Token | Value | Description | tier |
|---|---|---|---|
--cem-gap-related | var(--cem-dim-x-small) | Siblings in the same unit (8px) | recommended |
--cem-gap-group | var(--cem-dim-small) | Items in the same group, distinct (12px) | recommended |
--cem-gap-block | var(--cem-dim-medium) | Between groups/blocks inside one surface (16px) | recommended |
--cem-gap-section | var(--cem-dim-large) | Between major sections (24px) | recommended |
--cem-gap-page | var(--cem-dim-x-large) | Between page-level regions (32px) | recommended |
Guideline: if users perceive two things as "one unit," use gap-related. If they perceive "these are separate
things," move up to gap-group or gap-block.
Important: for interactive adjacency, apply the D2 guard contract (
gap = max(D1 gap, D2 guard)).
Use when padding content within a container.
| Token | Value | Description | tier |
|---|---|---|---|
--cem-inset-control | var(--cem-dim-x-small) | Smallest safe inset for tight controls (8px) | recommended |
--cem-inset-container | var(--cem-dim-medium) | Default inset for common containers (16px) | recommended |
--cem-inset-surface | var(--cem-dim-large) | Comfortable inset for reading surfaces and prominent cards (24px) | recommended |
Cross-reference: When combining insets with bend (D3), ensure inset is large enough to prevent content crowding in
rounded corners. See cem-shape.md §8.5 for guidance.
These tokens are defined and governed in D2 (cem-coupling.md). They are listed here as a *
normative constraint*
because they bound how far D1 spacing modes can compress interactive adjacency. Do not set or tune these in D1;
treat them as sourced from the D2 theme.
:root {
/* Minimum distancing between adjacent operable zones (prevents interference). */
--cem-coupling-guard-min: 0.5rem; /* nominally 8px */
/* Minimum operable zone (layout-level). Keep invariant across modes. */
--cem-coupling-zone-min: 3rem; /* nominally 48px @ 16px root */
/* Invisible expansion beyond visuals (halo). */
--cem-coupling-halo: 0.25rem; /* nominally 4px */
/* Legacy aliases (deprecated): keep only while migrating older code */
--cem-touch-separation-min: var(--cem-coupling-guard-min);
--cem-touch-target-min: var(--cem-coupling-zone-min);
}
Interpretation (summary):
This is distinct from UI rhythm.
| Token | Value | Description | tier |
|---|---|---|---|
--cem-rhythm-reading-paragraph | 0.75em | Default paragraph spacing for prose | recommended |
--cem-rhythm-reading-section | var(--cem-dim-large) | Space between reading sections within one surface (24px) | recommended |
Cross-reference: Reading rhythm must be validated against typography tokens. See
cem-voice-fonts-typography.md.
Optimized for scan/compare flows (tables, metrics).
| Token | Value | Description | tier |
|---|---|---|---|
--cem-rhythm-data-row | var(--cem-dim-x-small) | Table-like, scan-first row padding (8px) | recommended |
--cem-rhythm-data-group | var(--cem-dim-medium) | Scan grouping — subtotals, row groups, metric clusters (16px) | recommended |
These describe layout structures without baking in specific components.
Default vertical stack gap for general UI stacks. Optional tight/loose variants for systems that need explicit range.
Default cluster gap for inline groups (icons + text, toolbars, button clusters). The legacy --cem-layout-inline-*
aliases are deprecated — use --cem-gap-* instead.
These are responsive and should be treated as semantics, not fixed numbers.
| Token | Value | Description | tier |
|---|---|---|---|
--cem-layout-stack-gap | var(--cem-gap-block) | Default vertical stack gap for general UI stacks | required |
--cem-layout-cluster-gap | var(--cem-gap-related) | Default cluster gap for inline groups | required |
--cem-layout-gutter | var(--cem-dim-medium) | Default content gutter (16px) | required |
--cem-layout-gutter-wide | var(--cem-dim-x-large) | Wide-screen gutter (32px) | recommended |
--cem-layout-gutter-max | var(--cem-dim-xx-large) | Maximum gutter (64px) | recommended |
--cem-layout-stack-tight | var(--cem-gap-related) | Tight stack variant — rare, use only when explicitly tighter than default | optional |
--cem-layout-stack-loose | var(--cem-gap-section) | Loose stack variant — rare, use only when explicitly looser than default | optional |
--cem-layout-inline-tight | var(--cem-dim-x-small) | Deprecated — use --cem-gap-related | deprecated |
--cem-layout-inline | var(--cem-dim-small) | Deprecated — use --cem-gap-group | deprecated |
--cem-layout-inline-loose | var(--cem-dim-medium) | Deprecated — use --cem-gap-block | deprecated |
Material often frames this as "density." In CEM, treat this explicitly as a D1 spacing mode that shifts Space & Rhythm while preserving D2 coupling invariants.
:root {
/* dense | normal | sparse */
--cem-layout-spacing: normal;
}
Mode intent summary:
| D1 spacing mode | Primary goal | What changes | What must not change |
|---|---|---|---|
dense | Increase information density | Step down gaps/insets/gutters ~1 step where safe | D2 coupling invariants (zone/guard), and any gap that separates interactive affordances must still satisfy gap = max(D1 gap, D2 guard) |
normal | Baseline | Use canonical endpoints as defined | Same invariants |
sparse | Increase calm/readability | Step up gaps/insets/gutters ~1 step | Same invariants |
Hard constraints (never override):
--cem-coupling-zone-min--cem-coupling-guard-min (may force some "gaps" to stay at or above the minimum)--cem-coupling-halo (must be considered when validating dense clusters)normal mode uses the baseline values defined in §6–7. dense steps gaps down ~1 scale step; sparse steps them up ~1
step. Tokens not in this table are unaffected by spacing mode.
| Token | dense | sparse |
|---|---|---|
--cem-gap-related | var(--cem-dim-x-small) | var(--cem-dim-small) |
--cem-gap-group | var(--cem-dim-x-small) | var(--cem-dim-medium) |
--cem-gap-block | var(--cem-dim-small) | var(--cem-dim-large) |
--cem-gap-section | var(--cem-dim-medium) | var(--cem-dim-x-large) |
--cem-gap-page | var(--cem-dim-large) | var(--cem-dim-xx-large) |
--cem-inset-control | var(--cem-dim-x-small) | var(--cem-dim-small) |
--cem-inset-container | var(--cem-dim-small) | var(--cem-dim-large) |
--cem-inset-surface | var(--cem-dim-medium) | var(--cem-dim-x-large) |
--cem-layout-gutter | var(--cem-dim-small) | var(--cem-dim-large) |
--cem-layout-gutter-wide | var(--cem-dim-large) | var(--cem-dim-xx-large) |
--cem-layout-gutter-max | var(--cem-dim-x-large) | var(--cem-dim-xxx-large) |
--cem-rhythm-reading-paragraph | 0.6em | 1em |
--cem-rhythm-reading-section | var(--cem-dim-medium) | var(--cem-dim-x-large) |
--cem-rhythm-data-row | var(--cem-dim-xx-small) | var(--cem-dim-small) |
--cem-rhythm-data-group | var(--cem-dim-small) | var(--cem-dim-large) |
Components should bind to semantic endpoints (gap-*, inset-*, rhythm-*) and inherit spacing mode automatically.
If a component is interactive and uses layout gaps between peer affordances, resolve its gap as:
gap = max(D1 semantic gap, D2 coupling guard).
If a component must remain stable across spacing modes (rare), bind it to reference steps explicitly and document why.
Use these patterns so teams can implement screens without inventing spacing semantics.
Stacks (vertical composition)
--cem-layout-stack-gap (maps to --cem-gap-block).--cem-gap-related.Clusters (horizontal composition)
--cem-layout-cluster-gap (maps to --cem-gap-related).gap: max(var(--cem-layout-cluster-gap), var(--cem-coupling-guard-min)).Cards / panels / surfaces
--cem-inset-container.--cem-inset-surface.--cem-inset-control (only when content remains legible).cem-shape.md §8.5.Sections and page regions
--cem-gap-block.--cem-gap-section.--cem-gap-page.Gutters (responsive framing)
--cem-layout-gutter.--cem-layout-gutter-wide.--cem-layout-gutter-max.Reading and data cadence
--cem-rhythm-reading-paragraph and --cem-rhythm-reading-section.--cem-rhythm-data-row and --cem-rhythm-data-group.--cem-dim-*) as the foundation.gap-*, inset-*, rhythm-*) to dimension tokens.zone/guard/halo) as hard constraints for interactive adjacency — see
cem-coupling.md.data-cem-spacing="dense|normal|sparse" only if product needs spacing modes.cem-shape.md §8.5.This excerpt is provided for convenience when working in D1. The canonical safety definitions and governance live in
D2. Coupling & Compactness (cem-coupling.md); visual geometry values live in D2c.
Controls (cem-controls.md).
| Coupling mode | Product intent | D2c Controls visual geometry trend | D2 halo (--cem-coupling-halo) trend | Typical surfaces |
|---|---|---|---|---|
forgiving | Minimize mis-coupling for imprecise input | Larger controls/rows; more internal padding | Smaller (visuals already meet zone) | mobile-first, kiosks, accessibility-first, gaze/dwell |
balanced | Default across modalities | Baseline control heights/rows | Baseline | mixed pointer + touch, general app UI |
compact | Increase information density without breaking operability | Smaller visuals; reduced chrome | Larger (use halo to preserve zone) | data grids, admin tools, scan-heavy panels |
Normative invariants (do not override in D1):
--cem-coupling-zone-min is mode-invariant.--cem-coupling-guard-min is mode-invariant.gap = max(D1 semantic gap, D2 coupling guard).This D1 spec is a contract. Changes must be intentional, reviewable, and versioned.
Treat as major (breaking) if you:
gap-*, inset-*, layout-*, rhythm-*).gap-group stops meaning "within a group").Treat as minor/patch if you:
D1 is subordinate to D2 for operability. The following must hold in every release:
gap = max(D1 semantic gap, D2 coupling guard).Interactive-adjacency contract (non-normative for generator): any consumer using D1 gaps between interactive affordances must resolve the effective gap as:
gap:
max
(
var
(
--cem-gap-related
)
,
var
(
--cem-coupling-guard-min
)
)
;
The generator does not enforce this — it is component-author responsibility, documented in the manifest notes above.
Reading-rhythm validation is deferred to D6 cross-check (Phase 12 in the implementation plan). D1 cannot validate rhythm in isolation.
Token tier is encoded in the tier column of each source table. The manifest validator derives expected token names
from these tables using the same XPath pattern as cem-dimension.html.
| Source table h6 id | Tokens covered | Validator derivation |
|---|---|---|
cem-dim-scale | --cem-dim-* (8 tokens) | one token per row |
cem-dim-gaps | --cem-gap-* (5 tokens) | one token per row |
cem-dim-insets | --cem-inset-* (3 tokens) | one token per row |
cem-dim-rhythm-reading | --cem-rhythm-reading-* (2 tokens) | one token per row |
cem-dim-rhythm-data | --cem-rhythm-data-* (2 tokens) | one token per row |
cem-dim-layout | --cem-layout-* (10 tokens; deprecated tier omitted from default output) | one token per row |
Spacing mode overrides (cem-dim-spacing-overrides) produce no new token names — they override existing tokens within
:root[data-cem-spacing="dense"] and :root[data-cem-spacing="sparse"] selectors.
Local CEM documentation
Control size belongs to D2c Controls,
while D2 Coupling owns operability and D1 owns layout spacing.
Do not map control size="large" to the D1 spacing token --cem-dim-large.
2.5rem visual
minimum); content and container flex/grid allocation determine the final box.2.5rem / 3rem / 4rem / 6rem / 8rem. Large profiles support vertical rhythm
when actions participate in layouts. They do not fix width or cap growth.data-cem-size; action exposes size as a local override.
Removing it restores inheritance. Other controls can adopt this shared pattern.--cem-coupling-zone-min in both axes
(currently 3rem). Small requested geometry reserves a transparent hit border
inside that box; profiles meeting the minimum need no expansion. This includes
the omitted/default and compact-coupling cases, not just explicit small.The 3rem target is CEM policy, not a claim of automatic ADA conformance. At a
16px root it is 48 CSS pixels; consumers must assess actual computed dimensions.
Size expresses available space and prominence. Content type suggests useful
examples, but does not select size automatically. Keep medium as the API
spelling for an ordinary standalone action; normal is not an additional value.
| Profile | Typical use | Minimum visible height |
|---|---|---|
| Omitted | Content-sized or container-sized action; inherits theme geometry | Inherited |
small | Inline action, table-row control, compact toolbar icon | 2.5rem |
medium | Standalone text action, text with icon, ordinary icon action | 3rem |
large | Prominent confirmation or large playback control | 4rem |
x-large | One choice represented by an image and title | 6rem |
xx-large | Featured choice or CMS hero composition with one action | 8rem |
An icon-only action can use any profile. Glyph size, content layout, intent,
coupling and control height remain independent. The xx-large baseline continues
the 4/6/8rem progression for large controls; it is a starting minimum, not a
fixed hero height or a reuse of the D1 --cem-dim-xx-large spacing token.
Images, wrapping text and responsive container layout determine the final size.
Only requested geometry below the coupling minimum adds the transparent hit
border. Medium and larger default profiles add none, including tiles and heroes.
An overridden profile below that minimum still receives the safety inset.
Compose image/title content through the default slot using noninteractive phrasing content, such as spans, images and strong text. The action permits label wrapping; the author owns stacked or responsive layouts inside the projected content. Size never silently changes row/column layout, font scale or image size. Keep content readable under zoom and narrow containers rather than clipping it to a preset height. A small profile stretched by layout retains its reserved compact inset; this policy follows requested geometry, not DOM measurement.
Semantics follow behavior at every size: a command uses a native button and a
destination uses a native link. The current cem-action owns a native button;
the image and hero playground samples therefore choose content, not navigate.
Adding native-link support is a separate component contract change. A card with
an interactive preview or several actions keeps those controls separate; do not
nest them inside one action. The legacy material demo-list-card follows this
composition: the preview is a sibling of a title/description link with a 6rem
minimum height.
This decision draws on Angular Material buttons, Material 3 Expressive button sizes, icon button sizes, and Angular Material card composition. Material offers size choices for both text and icon buttons; CEM retains its own theme scale and coupling minimum.