Spacing

Spacing tokens set the padding and gaps in Nuton components and layouts. Use them for every inset, gap and offset, so screens keep one rhythm.

  • Beta
  • Accessibility
  • Figma Spacing collection pending (interim)
  • packages/tokens/src/interim/radius-spacing.json

Beta because every space-* token (16) is interim: Figma binds almost no spacing (0.2% of gaps), so the scale is the set of values observed on Nuton screens. Stable needs a Figma Spacing collection (D9 backlog, D20) and the P1 padding update in Figma (FC-08). There are no code-only spacing tokens.

Overview

One scale covers component insets, the gaps between elements and the space around sections. It's built on 4px: space-N is N × 4px, so the names match Tailwind's default spacing numbers.

  • Whole steps (0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20) are the layout scale: gaps between components, card and panel padding, space between sections.
  • Half steps (0-5, 1-5, 2-5, 3-5) add 2px increments below 16px. Use them inside components, where a 4px jump is too coarse for optical balance: button insets, icon gaps, focus offsets.
  • The scale stops at 80px. Larger distances are layout decisions (grid, breakpoints), not spacing tokens.

Interim: spacing has no Figma home yet

Under D20, design tokens are authored in Figma. Spacing is the exception for now: Figma binds almost no gaps or padding, so these values come from what Nuton screens actually use (docs/token-architecture.md, Spacing proposal). Every token is flagged nuton.interim and the token check warns about it on every run. When the DS Owner adds a Figma Spacing collection (D9 backlog), the tokens move into the Figma snapshot and the names stay the same.

Tokens

Every spacing token in @nuton/tokens, in generated order: whole steps first, then half steps. The bar shows the value at full size.

Spacing tokens
PreviewTokenValueSource
--nu-space-00pxInterim
--nu-space-14pxInterim
--nu-space-28pxInterim
--nu-space-312pxInterim
--nu-space-416pxInterim
--nu-space-520pxInterim
--nu-space-624pxInterim
--nu-space-832pxInterim
--nu-space-1040pxInterim
--nu-space-1248pxInterim
--nu-space-1664pxInterim
--nu-space-2080pxInterim
--nu-space-0-52pxInterim
--nu-space-1-56pxInterim
--nu-space-2-510pxInterim
--nu-space-3-514pxInterim

Source: packages/tokens/src/interim/radius-spacing.json. All values are global tokens; there are no semantic spacing aliases yet. Components wrap them in their own variables, such as --nu-button-pad-inline.

Usage

CSS variables

Load @nuton/tokens/tokens.css once, then reference the variables in any property that takes a length.

css
.lesson-card {
  display: grid;
  gap: var(--nu-space-3);
  padding: var(--nu-space-5);
}

/* Subtract a 1px border so the outer size stays on the scale (as Input does) */
.space-chip {
  border: 1px solid var(--nu-stroke-soft-200);
  padding-inline: calc(var(--nu-space-3) - 1px);
}

Tailwind

The Tailwind v4 theme and v3 preset in @nuton/tokens expose semantic colours, the type scale, radius, shadows, easing and breakpoints. They don't generate spacing utilities. Reference the variables with Tailwind's arbitrary-value syntax instead. Tailwind's built-in spacing numbers line up with the token names, but they aren't bound to the tokens, so they won't follow a future change to the scale.

tsx
<div className="grid gap-(--nu-space-3) p-(--nu-space-5)">
  …
</div>

Inside @nuton/ui, a lint rule blocks arbitrary Tailwind values: components set spacing in their CSS through --nu-space-* and their own component variables.

Choosing a token

  • Inside a control, use the component's variables (--nu-button-pad-inline, --nu-input-pad-inline). Override them on a wrapper only for a documented exception.
  • Related items sit closer than unrelated ones. Field parts are space-1 (4px) apart; actions in a row use space-2 (8px); cards in a grid use space-3 or space-4.
  • Insets grow with the control. Bigger controls get bigger insets, and an icon edge sits closer to the border than a text edge (P1).
  • Stay on the scale. If a value isn't a token, round to the nearest step. When a border would push a size off the scale, subtract it with calc().

In use

Spacing decisions in real components. P1, approved at the owner's request, is the clearest example: the padding on buttons and inputs was rebalanced so insets scale with size, while every height stayed the same.

Button insets

sm · 10px icon edge · 14px text edge
md · 12px icon edge · 16px text edge
lg · 16px icon edge · 20px text edge
Button padding tokens by size
SizeHeightBlock insetText-edge insetIcon-edge insetIcon ↔ label gap
sm36space-2 · 8pxspace-3-5 · 14pxspace-2-5 · 10pxspace-1 · 4px
md44space-3 · 12pxspace-4 · 16pxspace-3 · 12pxspace-1-5 · 6px
lg52space-4 · 16pxspace-5 · 20pxspace-4 · 16pxspace-2 · 8px

Heights are measured in the browser (see Button). Figma uses one fixed text inset at every size, so large buttons looked cramped and small ones loose; P1 replaced it with the steps above. Figma still shows the old padding (FC-08).

Input insets

Input and field spacing tokens
PartTokenNote
Inline padding, large (48)space-4 · 16pxP1. Figma uses a larger inset at both sizes.
Inline padding, small (36) and inline appearancespace-3 · 12pxP1. The 1px border is subtracted, so the content edge lands on the token.
Icon ↔ text gapspace-2 · 8pxUnchanged from Figma.
Label ↔ control ↔ hintspace-1 · 4pxP1 amended: follows Figma again.
Label row heightspace-6 · 24pxFigma Label 4446:59279.

Gap between actions

Accessibility

Spacing decides how big a target is and how close it sits to the next one. WCAG 2.2 AA (2.5.8 Target size, minimum) asks for targets of at least 24 × 24 CSS pixels.

A smaller target still passes if a 24px circle centred on it doesn't overlap another target or that target's circle. So when a control can't grow, the gap around it has to. space-6 (24px) is the minimum target size, which makes it a useful check: a control plus its share of the surrounding gap should reach it.

Smallest target per component against WCAG 2.5.8
ComponentSmallest target (measured)2.5.8 result
Button sm, icon-only36 × 36Passes
Input sm and inline36 tallPasses
Button group 2xs, icon-only22 × 22, edge to edgeFails (axe target-size, C26)
Link button md / sm20 / 16 tall, no paddingPasses only inline in a sentence or with enough space around it (C28)

Open: two components need a spacing decision (C26, C28)

C26: 2xs icon-only Button group items need either larger targets or a gap between them. C28: Link button has no padding, so its target depends on the space you leave around it. Both wait on a design decision; until then, don't use 2xs for icon-only groups, and don't place link buttons side by side without a gap.

  • Spacing tokens are in px. They scale with browser zoom, which is what WCAG 1.4.4 and 1.4.10 test.
  • Space is never the only grouping cue for meaning: a field's label and hint are tied to the control with aria-labelledby and aria-describedby, not just placed nearby.

You must: keep every target at 24 × 24 or give it enough space around it; check new components with axe's target-size rule.

Guidelines and related

Use spacing tokens for

  • Padding, gaps and margins in product screens and components.
  • Half steps inside components, where a control needs optical balance.
  • Making room around small targets so they meet 24 × 24.

Don't use them for

  • Component heights. Those are fixed per size (Button 36/44/52, Input 48/36).
  • Page-level layout widths. Use the layout and breakpoint rules.
  • Overriding a component's padding to make it fit. Pick another size instead.

Do and don't

DoMatch control sizes in a row. A small input and a small button share a height and an inset step, so their edges line up.
Don'tDon't mix sizes to save space. Mismatched heights and insets break the rhythm of the row.
DoLeave a gap between small targets. Link buttons have no padding, so the space between them is what keeps each one easy to hit (C28).
Don'tDon't pack small targets together. With no gap, 16px-tall links crowd each other and fail the 24 × 24 spacing check.
DoUse a size whose targets meet the minimum. At xs, icon-only items stay above 24 × 24.
Don'tDon't put tiny targets edge to edge. At 2xs, icon-only items are 22 × 22 with no gap, which fails WCAG 2.5.8 (C26).