> ## Documentation Index
> Fetch the complete documentation index at: https://substrate.docs.unknowncreatives.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Spacing and Density in a Substrate Brand Config

> One space unit, multiplied at each use site by the runtime density and scale factors. Density is a continuous slider, and preset names are brand-defined.

Substrate has no spacing scale — no numbered token ladder, and no table of multiples to memorize. A brand declares a single unit, and every use site multiplies it by its own coefficient and by the runtime density and scale factors, which means changing density is a live preference change rather than a rebuild.

## The space block

One field. The bundled demo brands all use the same value:

```yaml theme={null}
space:
  unit: 4
```

`unit` is a bare number in pixels, nested under `space`. It emits as `--space-unit: 4px` on the brand selector.

## How spacing is applied

Instead of consuming a named token, a component multiplies the unit by its own coefficient inside a `calc()`. From `generated/global/css/components/badge/badge.gen.css`:

```css theme={null}
.sub-badge[data-ucs] {
  /* 6px at default density=1, scale=1, space-unit=4 */
  --surface-gap-x: calc(var(--density) * var(--scale) * var(--space-unit) * 1.5);
  /* 8px at default density=1, scale=1, space-unit=4 */
  --surface-padding-x: calc(var(--density) * var(--scale) * var(--space-unit) * 2);
  /* 2px at default density=1, scale=1, space-unit=4 */
  --surface-padding-y: calc(var(--density) * var(--scale) * var(--space-unit) * 0.5);
}
```

Two things follow from this shape that a ladder cannot give you. Coefficients are **fractional** — the badge's `0.5` and `1.5` are half and one-and-a-half units, positions no integer scale contains. And every value re-resolves whenever `--density` or `--scale` changes, with no regeneration, because the multiplication happens in the browser rather than in the pipeline.

Corner radius rides the same factor. From `generated/global/css/tokens.gen.css`:

```css theme={null}
:root {
  --radius: calc(var(--radius-base) * var(--density));
}
```

So a denser layout gets tighter corners automatically, keeping the shape language proportional to the spacing around it.

## Density is continuous

`densityFactor` is an axis on the runtime preference vector, running from 0.8 to 1.3. It is not a mode, not an enum, and not a build input. A brand bounds how far users may move it with the `flexibility.density` block:

```yaml theme={null}
flexibility:
  density: { min: 0.8, max: 1.3 }
```

Most of the bundled demo brands use exactly that range; one opens the lower bound slightly to `0.75`.

## Density presets are named positions, and brands name them

`presets.density` is an **open map** of named levels, each declaring where it sits on the continuous factor. Most brands use the conventional three:

```yaml theme={null}
presets:
  density:
    compact:
      density-factor: 0.85
    default:
      density-factor: 1.0
    comfortable:
      density-factor: 1.15
```

But the names are the brand's to choose. Imagine the fictional Acme brand serving two contexts from one config — a marketing surface and a product surface — and naming its density levels after them rather than after their tightness:

```yaml theme={null}
presets:
  density:
    discovery:
      density-factor: 1.15
    default:
      density-factor: 1.0
    workspace:
      density-factor: 0.85
```

`discovery` is the roomy marketing setting and `workspace` the tight product one. Same continuum, same factors as the conventional trio — different vocabulary, chosen because it matches how a team talks about its surfaces.

<Note>
  Because these are presets over a continuum rather than an enumeration of allowed states, a user is never limited to the named levels. Any value inside the brand's `flexibility.density` bounds is reachable, and the presets are convenience points on the way.
</Note>

A preset level can also carry property deltas that apply while it is active — a font-weight nudge, a padding adjustment — using the same additive (`"+0.05"`) or replacement (bare value) convention as component config. That lets a density level do more than move one number, when a brand needs it to.

## Changing density

Nothing rebuilds. The runtime writes `--density`, every `calc()` that references it re-resolves, and layout reflows — components, radii, and any density-linked type coefficients together. A brand switching a surface from `workspace` to `discovery` is setting a preference, not running a pipeline.

<Warning>
  If you are porting from a system with a numbered spacing ladder, resist recreating one on top of `--space-unit`. Named steps would freeze the coefficients at generation time and cut the density factor out of the loop, which is the one thing this model exists to prevent.
</Warning>

<CardGroup cols={2}>
  <Card title="Typography" icon="text-size" href="/brand-config/typography">
    The same continuous treatment applied to type, with fractional role exponents.
  </Card>

  <Card title="Brand Config Overview" icon="sliders" href="/brand-config/overview">
    The full schema, the required sections, and the presets block in context.
  </Card>
</CardGroup>
