Spacing & density

Spacing is the scale; a density is a step on it that the theme moves at each breakpoint.

Edit a shared spacing token and see its consumers update together.

Try it: one token, two paddings and a gap

This demonstration uses direct Spacing to show the base scale: these paddings and gaps intentionally keep a stable value across breakpoints. Prefer Density when building ordinary component spacing.

The colored areas below use the page’s actual CSS variables. Edit space(4) to update both boxes and the gap between the action items.

Card: padding: space(4)
Content
Panel: padding: space(4)
Content
Actions: gap: space(4)
FirstSecond

These edits update the playground’s custom theme and persist spacing overrides in this browser when storage is available. Other UI using the token may also change. They do not write to your source JSON file. The reference examples above stay unchanged.

Every spacing token (16): as rings, and as editable fields

Concentric Spacing Visualization

Each ring shows a spacing level measured from the same content. These are alternative distances, not nested paddings added together. Click a ring to edit its token, or use the labeled token fields below. Custom values determine ring size; token numbers alone do not guarantee size order.

Content
space(1)
space(2)
space(3)
space(4)
SpacingUsage.uxdsl
.any-class {
  padding: space(4);
}
space(1)
space(2)
space(3)
space(4)
space(5)
space(6)
space(7)
space(8)
space(9)
space(10)
space(11)
space(12)
space(13)
space(14)
space(15)
space(16)

Spacing defines a value; Density defines how spacing responds to the viewport. Prefer density(n) for component spacing using the theme’s responsive mapping. Use space(n) directly when a stable value across breakpoints is intentional. Changing that mapping updates every consumer of the token without changing component code.

  1. Define how a spacing token changes across breakpoints in your theme JSON.
  2. Use that Density token wherever components should share the same responsive spacing.
  3. Edit one mapping below and watch both connected boxes update together.

Density is the recommended default for component spacing. Standard CSS remains available when finer control is needed. The generated media queries belong to the system; they are not removed from CSS.

One token. Responsive spacing everywhere.

UXDSL builds on standard CSS. Spacing defines the base spacing values, and Density makes that spacing responsive. Prefer Density for component spacing, use Spacing when a stable value is intentional, and use CSS directly when finer control is needed. This is the recommended authoring approach, not a compiler restriction.

A Density token maps a level to spacing values at different breakpoints. The 4 in density(4) identifies a token; it does not mean four pixels or a multiplier. You define its progression.

1. Define the behavior in your theme JSON

This example is a theme excerpt. Density reuses the spacing scale instead of introducing a separate set of measurements.

{
  "breakpoints": { "xs": 0, "md": 768, "xl": 1280 },
  "spacing": {
    "4": "0.75rem",
    "5": "1rem",
    "6": "1.5rem"
  },
  "densities": {
    "4": "xs(space(4)) md(space(5)) xl(space(6))"
  }
}
How this example resolves at each viewport width
Viewportdensity(4)space(4)
Below 768pxspace(4) → 0.75rem0.75rem
768–1279pxspace(5) → 1rem0.75rem
1280px and abovespace(6) → 1.5rem0.75rem

A rule remains active until another defined breakpoint overrides it. The progression is configurable: spacing can increase, decrease or stay the same.

2. Use it in your components

UXDSL you write
.card, .panel {
  padding: density(4);
}

The card and panel choose a token. Its responsive behavior lives in the theme.

Equivalent plain CSS
/* Base spacing tokens from the theme */
:root {
  --uxdsl__space__4: 0.75rem;
  --uxdsl__space__5: 1rem;
  --uxdsl__space__6: 1.5rem;
  --uxdsl__density__4: var(--uxdsl__space__4);
}

@media (min-width: 768px) {
  :root { --uxdsl__density__4: var(--uxdsl__space__5); }
}

@media (min-width: 1280px) {
  :root { --uxdsl__density__4: var(--uxdsl__space__6); }
}

.card, .panel {
  padding: var(--uxdsl__density__4);
}

You can build this same behavior in plain CSS using responsive custom properties. UXDSL generates those rules from the theme, so you maintain one mapping instead of repeating breakpoint decisions in each component. Less repeated source code does not necessarily mean less generated CSS.

3. Change one token. Update both boxes.

These live boxes use this page’s current theme and actual browser viewport. The colored area is padding. Edit the active rule for Density 4 to see both responsive boxes change; the fixed box continues to use space(4).

Live density(4): xs(space(4)) md(space(5)) xl(space(6))

Cardpadding: density(4)
Content
Panelpadding: density(4)
Content
Fixed spacingpadding: space(4)
Content

Resize your browser to see the responsive boxes follow the mapping. The JSON and CSS above remain a reference example; the live definition shows your edits. Edits are a local demo and are not saved to your theme file.

Two ways to update the system: change a spacing value to update every reference to that spacing token, or change a Density mapping to update every consumer of that Density token. Component declarations stay the same.

Prefer density() for component spacing. Use space() directly for an intentional stable value, or CSS for finer control. A Spacing value remains editable through the theme. Density coordinates spacing; layout changes such as columns and navigation still need their own responsive decisions.

Russian Doll Visualization

Each ring compares a Density level measured from the same content. The rings are not nested component paddings added together. Click a ring to edit its token. This diagram responds to the main browser viewport.

Content
density(1)
density(2)
density(3)
density(4)
DensityUsage.uxdsl
.any-class {
  padding: density(4);
}
Every density token, its progression and an editor
density(1)
xs:space(1)
md:space(2)
xl:space(3)
xs
md
xl
density(2)
xs:space(2)
md:space(3)
xl:space(4)
xs
md
xl
density(3)
xs:space(3)
md:space(4)
xl:space(5)
xs
md
xl
density(4)
xs:space(4)
md:space(5)
xl:space(6)
xs
md
xl
density(5)
xs:space(5)
md:space(6)
xl:space(7)
xs
md
xl
density(6)
xs:space(6)
md:space(7)
xl:space(8)
xs
md
xl
density(7)
xs:space(7)
md:space(8)
xl:space(9)
xs
md
xl
density(8)
xs:space(8)
md:space(9)
xl:space(10)
xs
md
xl
density(9)
xs:space(9)
md:space(10)
xl:space(11)
xs
md
xl
density(10)
xs:space(10)
md:space(11)
xl:space(12)
xs
md
xl
density(11)
xs:space(11)
md:space(12)
xl:space(13)
xs
md
xl
density(12)
xs:space(12)
md:space(13)
xl:space(14)
xs
md
xl
density(13)
xs:space(13)
md:space(14)
xl:space(15)
xs
md
xl
density(14)
xs:space(14)
md:space(15)
xl:space(16)
xs
md
xl

Theme uxdsl.theme.json, merged over the base theme

{
  "spacing": {
    "4": "0.75rem",
    "5": "1rem",
    "6": "1.5rem"
  },
  "densities": {
    "4": "xs(space(4)) md(space(5)) xl(space(6))"
  }
}

UXDSL you write example.uxdsl

.card { padding: density(4); }
.toolbar { gap: space(4); }

CSS you get compiled by uxdsl; :root shows only these tokens

:root {
  --uxdsl__space__4: 0.75rem;
  --uxdsl__space__5: 1rem;
  --uxdsl__space__6: 1.5rem;
}
:root { --uxdsl__density__4: var(--uxdsl__space__4); }
@media (min-width: 768px) { :root { --uxdsl__density__4: var(--uxdsl__space__5); } }
@media (min-width: 1280px) { :root { --uxdsl__density__4: var(--uxdsl__space__6); } }
.card { padding: var(--uxdsl__density__4); }
.toolbar { gap: var(--uxdsl__space__4); }

Rules

UXDSL builds on standard CSS. Spacing defines the base spacing values, and Density makes that spacing responsive. Prefer Density for component spacing, use Spacing when a stable value is intentional, and use CSS directly when finer control is needed. This is the recommended authoring approach, not a compiler restriction.

  • space(k) is one step of the scale, the same at every width. density(k) is a step the theme moves at breakpoints — the base theme’s density(4) is space(4), then space(5) from md, then space(6) from xl.
  • A key is a name, not a number of pixels or a multiplier: density(2.5) is UXD_TOKEN_KEY, an undefined key is UXD_DENSITY_REFERENCE / UXD_SPACE_REFERENCE with the keys that exist.
  • Do not copy a density’s ladder into a component (padding: xs(space(4)) md(space(5))): the component stops following the theme. Change the density in the theme and every consumer follows.
  • space(0) and density(0) are an explicit zero. Spacing values are rem by default, so they still follow the root font size.
  • Changing spacing["4"] changes direct space(4) uses and every density, radius or typography value that references it; changing densities["4"] changes only density(4) uses.
.card { padding: density(4); }
.toolbar { gap: space(2); }
.card { padding: density(2.5); }  /* UXD_TOKEN_KEY: a key is a name, not a number to scale */

The guides for coding agents are on For AI agents.