Get started

Design tokens for responsive spacing: choose fixed, stepped, fluid, or container-scoped rules

Keep the primitive spacing scale stable by default. Only make a spacing decision responsive when a named layout or component must react to a defined condition. Put that condition in the narrowest layer that owns the behavior.

Updated October 1, 2026

Start with the controlling condition

A spacing scale defines the distances available for reuse. Responsive behavior answers a different question: under what condition should one of those distances change? Combining the questions too early produces names such as space-mobile, space-desktop, or space-responsive-lg. Those names expose an implementation guess without explaining the design requirement.

Start with the relationship you need to preserve. The gap between a button icon and label may need to remain stable wherever the button appears. A page shell may need more breathing room after the viewport reaches a recorded threshold. A section gap may grow gradually. A card may need a compact arrangement when its own column becomes narrow, even if the viewport is wide.

These are four different behaviors. Don't implement them by changing every primitive spacing value at one breakpoint.

Do not make the scale responsive by default

If --space-4 changes globally, every consumer inherits the change whether it needs it or not. Keep the primitive value stable and put the conditional decision in the semantic, layout, component, or CSS layer that owns the change.

Separate responsive spacing from nearby concerns

Several changes can make an interface look more or less spacious, but they don't share the same authority. Calling all of them responsive makes implementation and review less precise.

  • Viewport or container adaptation responds to available space. This is the responsive behavior covered here.
  • Density is a deliberate compact, comfortable, or touch-oriented variant. It can remain compact on a large viewport or comfortable on a small one.
  • A visual mode changes presentation under a theme or preference contract. Light and dark modes do not imply different spacing unless the product explicitly requires it.
  • Content-driven layout lets wrapping, intrinsic sizing, grid tracks, and document flow respond to content. A long title wrapping to another line does not automatically require another token.
  • A local exception is a reviewed departure for a named consumer. Record its reason, owner, and retest or removal trigger instead of disguising it as a reusable token.

Apply two checks. If instances with the same available space need different spacing because of density or product mode, responsiveness does not control the decision. If two instances share a viewport but need different arrangements because their containers differ, the viewport is not the right condition.

Freeze the source contract before writing CSS

Record the consumer, condition, values, owner, exclusions, and expected behavior before implementation. For stepped rules, include the complete ranges. Otherwise, the CSS author has to invent the thresholds.

Source version: design-system 2.4
Spacing decision: page-shell inline gutter
Named consumer: authenticated dashboard shell
Controlling condition: viewport inline size
Owner: application layout
Ranges:
  below 48rem: space-4
  48rem through below 80rem: space-6
  80rem or above: space-8
Fallback before any media query matches: space-4
Expected behavior:
  minimum representative width: space-4
  intermediate representative width: space-6
  maximum representative width: space-8
Exclusions:
  - card internal padding
  - compact-density tables
  - embedded widgets
Approved exceptions: none
Retest triggers:
  - source version changes
  - threshold contract changes
  - shell markup changes
Illustrative source contract. The names, values, and thresholds are examples, not an Identity Forge export or an executed project record.

This record is authoritative for the example that follows. It defines both transition edges and the fallback, while the exclusions protect unrelated consumers. A production record should replace the illustrative source version, values, and widths with approved project decisions.

Choose fixed, stepped, fluid, or container-scoped spacing

FixedSteppedFluidContainer-scoped
Controlling conditionNo responsive condition; the relationship remains stableA recorded viewport or layout thresholdContinuous change within explicit minimum and maximum boundsThe available size of a containing element
Best ownerPrimitive, semantic mapping, or component contractPage or layout contract plus conditional CSSLayout semantic or local CSS expressionReusable component contract and its container context
Suitable consumersControl padding, icon gaps, field internalsPage gutters, grid gaps, navigation layoutsSection rhythm, large layout gaps, display spacingCards, widgets, panels, and modules used in varied placements
Primary riskAdding needless variants to a stable relationshipUsing device labels instead of a real layout thresholdExpecting interpolation to make structural decisionsResponding to the viewport when the component's own space matters
Acceptance focusThe relationship stays stable in every required conditionEach exact threshold selects the recorded valueBounds and intermediate behavior match the expectationEach named container produces the intended component behavior
Choose the behavior from its controlling condition, then assign the narrowest owner whose change scope matches it.

These choices are not levels of sophistication. Fixed spacing is often the right answer. Fluid spacing is not inherently better than stepped spacing, and container queries do not replace viewport rules. A mechanism is useful only when its input matches the requirement.

Start from a complete spacing vocabulary

A design kit can supply stable spacing and layout decisions before a project assigns responsive behavior to named consumers. Browse the published kits, then document which conditions remain owned by your application.

Fixed spacing preserves a local relationship

Use fixed spacing when a relationship should remain recognizable across layouts. Common cases include the gap between an icon and label, padding inside a control, or the distance between a field label and input. The surrounding layout can move, wrap, or change width while the internal relationship stays stable.

:root {
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
}

.action-button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding-block: var(--space-3);
  padding-inline: var(--space-4);
}
Illustrative and unexecuted: the button consumes stable primitives and has no viewport rule.

Don't add a breakpoint merely because the page became narrower. If the button no longer fits, wrapping, label policy, an approved icon-only variant, or the parent layout may own the correction. Shrinking internal spacing can conceal the actual constraint.

Stepped spacing belongs to recorded layout transitions

Use stepped spacing when a layout deliberately changes at explicit thresholds. The source contract above assigns the dashboard shell three ranges: below 48rem, 48rem through below 80rem, and 80rem or above. Each range selects a value from the same stable primitive scale.

:root {
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --page-gutter: var(--space-4);
}

@media (min-width: 48rem) {
  :root {
    --page-gutter: var(--space-6);
  }
}

@media (min-width: 80rem) {
  :root {
    --page-gutter: var(--space-8);
  }
}

.dashboard-shell {
  padding-inline: var(--page-gutter);
}
Illustrative and unexecuted: the CSS implements the ranges already recorded in the source contract.

The semantic property --page-gutter owns the selection. The primitives remain stable, so a component consuming --space-4 does not change merely because the viewport crosses 48rem.

Behavior is more useful than a device label

Names such as mobile, tablet, and desktop are weak explanations for a threshold. Record the layout transition and exact condition even if a familiar device label remains in the project's vocabulary.

Fluid spacing interpolates within bounds

Use fluid spacing when continuous growth is the intended behavior. CSS clamp() takes a minimum, a preferred value, and a maximum. It can work for section rhythm or large display spacing where the design does not call for a hard jump.

:root {
  --section-gap: clamp(3rem, 2rem + 4vw, 6rem);
}

.marketing-page > section + section {
  margin-block-start: var(--section-gap);
}
Illustrative and unexecuted: the section gap uses a preferred expression while remaining between 3rem and 6rem.

The bounds are design decisions. The preferred expression controls what happens between them. Inspect the minimum-bound region, at least one interpolating condition, and the maximum-bound region.

Interpolation cannot decide when a grid gains a column, navigation changes composition, or a component switches layout. Those are structural decisions. Applying viewport units to every spacing value can also make related elements change at unrelated rates, so keep fluid rules selective and name their consumers.

Inspect the middle

Checking only the minimum and maximum confirms the bounds, not the intended behavior between them. Record an intermediate computed result or visible relationship, but don't treat an unrun example as evidence.

Container-scoped spacing follows component space

Use container-scoped spacing when a reusable component appears in placements with different available widths. A card in a narrow sidebar and the same card in a wide content grid can share a viewport while requiring different arrangements. Its containing element is the relevant condition.

.card-region {
  container-type: inline-size;
}

.summary-card {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-4);
}

@container (min-width: 40rem) {
  .summary-card {
    grid-template-columns: 1fr auto;
    gap: var(--space-6);
    padding: var(--space-6);
  }
}
Illustrative and unexecuted: the card responds to its containing region rather than the viewport.

MDN documents that a container size query requires a containment context. That context belongs in the integration contract. Record which element establishes it, which axis or feature is queried, and what the component does when the wider condition does not match.

Reusability alone does not justify a container query. Keep spacing fixed if the internal relationship should remain stable. Use a viewport-owned layout rule if the page changes as one coordinated structure.

Keep the portable artifact boundary honest

A token artifact can carry reusable values, types, groups, descriptions, and aliases. It doesn't automatically own project-specific media queries, container establishment, component markup, cascade order, or acceptance evidence.

The captured DTCG 2025.10 report describes a file format for exchanging design tokens between tools. Its status section identifies it as a preview draft and says not to implement or cite that draft as authoritative. The report can clarify the artifact boundary, but it is not runtime proof or a stable implementation target.

  • Source scale: stable reusable distances and their approved meaning.
  • Semantic or layout mapping: names such as page gutter or section gap that identify shared intent.
  • Component contract: spacing owned by one reusable component, including any container condition.
  • Conditional CSS: the media or container rule that selects or computes a value.
  • Approved exception: a named deviation with a reason, owner, and retest trigger.

Choose the narrowest owner whose change scope matches the requirement. A global source token is appropriate only when changing it should affect every intended consumer. A component-level decision belongs there only when that component needs independent change authority. Ordinary CSS may be the clearest home for a condition specific to one layout.

Verify representative conditions and exact edges

A valid token artifact and successful build are upstream evidence. They don't prove that the correct rule reached the correct element. Write down the expected result before inspection, then record what you observe without changing the expectation to fit the output.

Expected behaviorInspection criteriaFailure conditionDisposition
Minimum representative width: 20remThe shell uses --space-4Inspect computed inline padding and horizontal overflowAnother value resolves, or the shell causes unintended horizontal overflowOpen until observed
Lower edge: 47.99rem and 48rem47.99rem uses --space-4; 48rem uses --space-6Inspect both sides of the recorded 48rem thresholdEither width resolves to the wrong mappingOpen until observed
Intermediate representative width: 64remThe shell uses --space-6; protected component gaps remain stableInspect shell padding and the action-button icon gapThe shell uses another value or an excluded consumer changesOpen until observed
Upper edge: 79.99rem and 80rem79.99rem uses --space-6; 80rem uses --space-8Inspect both sides of the recorded 80rem thresholdEither width resolves to the wrong mappingOpen until observed
Maximum representative width: 100remThe shell uses --space-8 and does not continue growingInspect computed padding at 100rem and a wider required conditionThe gutter changes beyond the recorded maximum mappingOpen until observed
Content growthLong headings and translated labels wrap without replacing the gutter mappingUse required long content and inspect layout plus computed paddingContent causes an undocumented spacing override or unusable overflowOpen until observed
Direction changeLogical inline padding produces the intended start and end gutters in every required writing directionInspect each supported direction with the same consumer and conditionsPhysical-side assumptions reverse or distort the intended guttersOpen, or not applicable with a recorded reason
Illustrative acceptance matrix for the dashboard shell. The rows at 47.99rem, 48rem, 79.99rem, and 80rem verify the specified transition edges; the representative widths alone would not.

Include the source version, environment, named consumer, exact condition, expected value or relationship, observed result, approved exception, owner, retest trigger, and an accept, revise, or block decision. A note such as "looks good on mobile" can't be reused as precise evidence.

Source version: design-system 2.4
Consumer: authenticated dashboard shell
Environment: [record browser, version, and build]
Condition: viewport inline size = 48rem
Expected: --page-gutter resolves through --space-6
Observed: [fill after inspection]
Protected consumer: action-button icon gap remains --space-2
Exception: none approved
Owner: application layout
Retest trigger: source, threshold, shell, or mapping change
Disposition: accept | revise | block
Copyable acceptance record. Leave the observation unresolved until the named consumer has actually been inspected.

Route failures to the first divergent layer

When rendered spacing is wrong, don't begin by editing the nearest declaration. Trace the decision through the delivery chain and stop at the first layer that differs from the approved contract.

  1. 1

    Check the source decision

    Confirm that the behavior, values, ranges, version, owner, consumer, fallback, and exclusions are explicit.

  2. 2

    Inspect the emitted artifact

    Verify that the expected primitive or alias exists and that transformation did not replace, flatten, or omit it.

  3. 3

    Inspect the project mapping

    Confirm that the application maps the source value to the intended semantic or layout property and loads the correct version.

  4. 4

    Inspect the conditional rule

    Check the exact media or container condition, cascade order, fallback, and computed value at representative widths and transition edges.

  5. 5

    Inspect the component contract

    Confirm that the named consumer uses the shared mapping and that its structure or variant has not introduced a conflicting local value.

  6. 6

    Check approved exceptions

    Distinguish a documented exception from an accidental override. An undocumented override is a finding, not an exception.

  7. 7

    Compare the rendered consumer

    Record the observation, protected consumers, and disposition. Assign the correction to the owner of the first divergent layer.

This order prevents a local fix from concealing an upstream mismatch. It also protects unrelated consumers from a global token change made to solve one component's problem.

Where Identity Forge stops

Identity Forge kits can supply upstream spacing and layout decisions alongside DESIGN.md, Tailwind, shadcn, and DTCG delivery routes. This gives coding agents and implementation teams a consistent starting vocabulary. It does not prove that a consuming project chose suitable thresholds, established the required containers, mapped every value correctly, adopted shared components, preserved approved exceptions, or passed responsive review.

The consuming project owns its responsive conditions, mappings, component behavior, tests, exceptions, and approval. Treat a kit as the source of the decisions it actually contains, not as evidence about an application it has not inspected.

Take the most reused spacing decision in your project and classify it as fixed, stepped, fluid, or container-scoped before adding another conditional token. If you can't name its controlling condition, consumer, and owner, the token is not ready to create.

Sources