Spacing tokens — use-case guide
Every canvas carries a named spacing scale — seeded xs 4 · sm 8 · md 16 · lg 24 · xl 32 · 2xl 48 · 3xl 64, fully editable — and any spacing field (flex gap, grid row/column gap, padding, margin) can bind to a token instead of holding a loose number. Bound fields follow the token: change md once and every bound node across every page re-spaces, in one undo step. Bindings live beside the px values, so exports and code generation are untouched — the CSS is always real. Margin binds on ANY node type — text in a flex row is the classic case — where gap/padding are box-only.
Try it: draw a box (B), drop two boxes inside, select the parent, switch Display to Flex, and click the diamond at the end of the Gap field.
Reading the chrome
- Hollow diamond at a field's end — unbound; click to open the picker.
- Filled accent diamond + accent chip (
md) beside the value — bound. - Half-lit diamond, no chip — the selection (or a linked pair like padding X) disagrees; picking a token re-unifies.
- On canvas, bound padding/margin/gap bands tint accent and show the token name at full guide intensity. Unbound margin bands wear margin orange (the same ink as the Compass extension's box-model view), padding green, gap purple hatch.
Use cases
Bind a field to a token
- Click the diamond at the right end of any spacing input.
- The picker opens: rows in value order,
xs · 4through3xl · 64. - Hover (or arrow) down the rows — the canvas previews each token live.
- Click a row or press Enter.
The field shows the accent chip + the token's px. Binding + px land as one undo step. Multi-select fans the binding out to every selected box.
Preview without committing
Open the picker, hover to compare spacings, then press Escape or click outside. The canvas reverts exactly — previews never touch history.
Walk a bound field through the scale
- Scrub the field's label: the value steps token-to-token (16 → 24 → 32), chip following, never landing between tokens and never detaching. The whole scrub is one undo step.
- ↑/↓ in the field: one token per press. The ends of the scale hold — arrows never detach.
Detach
By declared intent, three ways:
- Type a number — explicit raw intent: detaches and takes the exact px.
- Alt-scrub — detaches and scrubs raw px (Alt trades away fine-scrub on bound fields only; spacing is integer px).
- Picker → Detach — keeps the current px, drops the link. Disabled (not hidden) when the field isn't bound.
Change a token's value everywhere
- Any picker → Manage tokens….
- Scrub or type the token's value.
- Every bound box on every page re-spaces live; release seals one undo step covering the table and all boxes. Escape mid-scrub rolls back.
Rename, add, delete tokens
In the manage view:
- Rename — click the name, type, Enter. Bindings survive renames — every chip re-labels and nothing orphans. Empty/duplicate names are rejected inline.
- Add —
+ Add tokenappears with the next free t-shirt name; the picker keeps rows value-sorted. - Delete — bound fields keep their current px and detach. One entry: undo restores the token and its bindings.
Drag spacing on the canvas
With a single node selected, grip pills appear for both of its roles (per the CSS box model — no display or flow gates): any box gets pills on every nonzero padding band, a flex box adds gap-band pills (grid gaps keep the grid editor's own pills, same math), and any node gets pills on every nonzero margin band, outside the box. A flex box inside a flex parent shows both sets — padding pills inside, margin pills outside.
- Drag — a live badge follows:
md · 16when magnetically snapped on-token (8 screen px radius), plain px between tokens. The growth mnemonic is one rule: dragging away from the box edge grows the spacing — inward for padding, outward for margin. - ⌥-drag (padding/margin pills) — symmetric: the opposite side follows, both sides becoming identical. Landing on a token rebinds BOTH sides; a raw frame writes both raw, detaching whichever was bound. Releasing ⌥ mid-drag restores the opposite side's starting px and binding exactly.
- ⌘-drag — raw px, no magnet; detaches if bound.
- Escape mid-drag — cancels and restores the exact starting values, opposite side included.
- Bound fields step the scale as you drag and stay bound; unbound fields snap to token values only — snap ≠ bind. Landing on 16 writes 16px; binding is always an explicit picker act, so casual drags can't quietly couple boxes to the scale.
Spacing of zero shows no pill — creation-from-zero stays in the inspector (no invisible hover zones).
Resize to the scale
Dragging a resize handle magnets the box's width/height onto token values — same 8-screen-px radius as the pills, per axis independently. The cursor badge speaks the pill vocabulary: md · 16 on a hit; a corner drag landing both axes on different tokens reads xl × lg (W × H order), a square landing on one token collapses back to md · 16. ⌘ resizes raw (no token or guide snap), a canvas-guide edge snap wins its axis, and shift (aspect/scale) never token-snaps. Snap ≠ bind holds here too — width/height are not bindable; a hit writes plain px.
Linked padding & margin
The Padding X field writes left+right, Y writes top+bottom; binding one binds both sides in one entry. Sides bound to different tokens read as diverged (half-lit diamond) — pick to re-unify or edit per-side. Both live in the "Padding & Margin" section, which renders for any selection per the CSS box model: padding on any box whatever its Display (greyed "Boxes only" for non-box selections), margin on any node — out of flow it composes with x/y (left/top position the margin edge). Margin clamps ≥ 0 like padding, with raw CSS in Other styles as the negative-margin escape hatch.
Co-editing
When several people share a canvas, token scrubs stream to same-page guests live; guests on other pages see the change land once, at release. Concurrent edits to the same token resolve last-writer-wins. View-only guests get an inert inspector.
Agents (MCP)
get_canvas_info returns spacingTokens; get_node_info includes any node's spacing bindings (margin binds on non-boxes too). An update_styles patch that writes raw px over a bound field clears the binding in the same batch — a binding never lies about its value; layout.margin per-side writes reject negatives (rawCss is the escape hatch). Token create/rename/delete and binding via MCP is deliberately not offered yet.
Interaction semantics
| Surface | Act | Effect | Undo / cancel |
|---|---|---|---|
| Field diamond | click | Opens picker (toggle) | — |
| Picker row | hover / arrows | Live preview on canvas | Escape/outside reverts exactly |
| Picker row | click / Enter | Binds token: binding + px | One step |
| Picker Detach | click | Clears binding, keeps px | One step |
| Bound field label | scrub | Steps the scale, stays bound | One step per scrub; Escape N/A (label scrub has no cancel, undo instead) |
| Bound field | ↑ / ↓ | One token per press; ends hold | One step per press |
| Bound field | type px | Detaches + writes px together | One step |
| Bound field label | Alt-scrub | Detaches + raw scrub | One step |
| Manage value | scrub / type | Table write + fan-out to all bound boxes | One step; Escape rolls back a live scrub |
| Manage name | type + Enter | Renames; bindings survive | One step; Escape cancels the edit |
| Manage delete | click | Removes token, detaches bound fields, px kept | One step restores both |
| Canvas pad/margin/gap pill | drag | Snaps to token values (badge md · 16); bound fields re-bind per step | One step; Escape restores exactly |
| Canvas pad/margin pill | ⌥-drag | Opposite side follows (identical value + binding) | One step covers both sides; Escape restores both; releasing ⌥ restores the opposite side |
| Canvas pill | ⌘-drag | Raw px; detaches if bound | One step; Escape restores |
| Resize handle | drag | W/H magnet to token values (badge md · 16; corners per-axis, xl × lg when both land) — snap ≠ bind, plain px; guide snap wins its axis | One step; Escape restores exactly |
| Resize handle | ⌘-drag | Raw px — no token or guide snap | One step |
Known limits (by design)
- Raw CSS in Other styles can visually contradict a binding — the same escape-hatch opacity as everywhere else.
- A remote guest on another page sees a token scrub land once at release, not per-frame.
- Concurrent edits to the same token: last write wins.