Spacing as first-class objects: tokens, patterns, and the gap is alive

Spacing gets the same treatment as typography — named tokens, a baseline
(space-md), and optical adjustments using token math. But the real shift:
spacing between objects is now a first-class object with its own ID in
page specs, organized in a Patterns section in the design system.

Key additions:
- Spacing Scale: 9 tokens (space-3xs to space-3xl), bring-your-own supported
- Type Scale: 9 tokens (text-3xs to text-3xl), semantic/visual separation
- Optical adjustments: token math (space-lg - space-3xs) with annotations
- Spacing objects in page specs: ↕ `id` — token, including space-zero
- Patterns section: organized by spacing value, context added on violation
- Agent reflection behavior: observe and reflect, don't interrogate
- Design loop: pattern recognition emerges from real design decisions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mårten Angner 2026-02-26 23:46:42 +01:00
parent c6bee85795
commit 47600dfb27
3 changed files with 140 additions and 2 deletions

View File

@ -97,7 +97,9 @@ Replace the table below with your system's spacing tokens. Any naming convention
### Option B: WDS default scale
Nine tokens, symmetric around `space-md`. Freya will propose pixel values during the first design session.
Nine tokens, symmetric around `space-md` (the baseline). Freya will propose pixel values during the first design session.
`space-md` is to spacing what `text-md` is to typography — the default you reach for first. It's the gap between paragraphs, between form fields, between list items. Everything else is relative to it: `space-sm` is tighter, `space-lg` is more generous.
| Token | Value | Use |
|-------|-------|-----|
@ -105,13 +107,36 @@ Nine tokens, symmetric around `space-md`. Freya will propose pixel values during
| space-2xs | — | Minimal spacing (badge padding, tight lists) |
| space-xs | — | Tight spacing (within compact groups) |
| space-sm | — | Small gaps (between related elements) |
| space-md | — | Default element spacing |
| **space-md** | — | **Default element spacing (the baseline)** |
| space-lg | — | Comfortable spacing (card padding, form fields) |
| space-xl | — | Section padding |
| space-2xl | — | Section gaps |
| space-3xl | — | Page-level breathing room |
### Optical adjustments
Sometimes the math is right but the eye says it's wrong. A circular image leaves white corners, a light element on a light background looks more spaced than it is. When this happens, use token math — not raw pixels:
```
space-lg - space-3xs → "standard spacing, pulled in by a hairline"
space-xl + space-2xs → "section padding, nudged out slightly"
```
In page specs, always annotate why:
| Padding top | **space-lg - space-3xs** (optical: circular image adds perceived whitespace) |
**Rules:**
- Adjustments always use token math: `base ± correction`
- Always annotate the reason — future readers need to know this wasn't a mistake
- If adjusting by more than one step, the base token is probably wrong — reconsider
In CSS: `calc(var(--space-lg) - var(--space-3xs))`
<!--
space-md is typically 16px (on an 8px grid) or 12px (on a 4px grid) — the most common
default spacing on the web. Same ballpark as body text size, which is not a coincidence.
The pixel values are yours to define. Common starting points:
2/4/8/12/16/24/32/48/64 (8px grid) or 4/6/12/16/24/32/48/72.
You can adjust later — specs stay valid because they reference names, not numbers.
@ -161,6 +186,63 @@ _Additional design tokens (colors, shadows, borders) will be documented here as
---
## Patterns
<!--
Patterns are organized BY the spacing, not by object pairs.
Each spacing value/composition accumulates a list of where it's used.
When the designer says "more space here", the agent fixes it, reflects
what it learned, and adds the situation to the relevant pattern below.
The agent does NOT ask "why?" — it observes and reflects:
"Got it — large image above a card row needs extra room. I'll remember that."
First occurrence: one-off fix in the page spec.
Second occurrence: agent applies the same pattern.
Third time: extract it here.
CONTEXT RULE: Entries are context-free by default (universal).
Add context only when the same object pair needs DIFFERENT spacing
in different situations. When that happens, BOTH entries get context
so you can tell them apart. Before the first violation, keep it clean.
-->
Spacing objects are first-class — they have IDs in page specs (e.g., `hem-heading-subtitle-gap`) and live here organized by value. Each spacing value accumulates the situations where it's used. The list grows from real design decisions.
_Patterns will be documented here as spacing objects recur across pages._
<!--
Example format (delete when real patterns emerge):
### space-sm
- Between heading and subheading
- Between icon and label inside buttons
- Between badge dot and badge text
### space-lg
- Card grid gap
- Between image and caption
- Between form field groups
### space-md
- Between image and caption (article page, inline photo — smaller image needs less room)
Note: space-lg also has "Between image and caption" — context added to both
because article pages are an exception to the universal rule.
### space-zero
- Vehicle icon bar flush against section below (homepage hero)
- Nav items touching divider lines (service menu)
### space-2xl / line / space-2xl
- Between content groups within a section when a visual break is needed
### space-xl + space-xs
- Below busy content blocks before card rows
-->
---
## Components
_Components will be documented here as patterns emerge across scenarios._

View File

@ -98,6 +98,8 @@ space-3xs space-2xs space-xs space-sm space-md space-lg space-xl space-2x
- If a spacing value isn't in the scale, it doesn't belong in the spec
- The scale can be adjusted as the project matures — specs stay valid because they reference names, not numbers
**Optical adjustments:** Sometimes the math is right but the eye says it's wrong — a circular image leaves white corners, a light element looks more spaced than it is. Use token math: `space-lg - space-3xs` (not raw pixels). Always annotate the reason. If adjusting by more than one step, the base token is probably wrong.
---
## Tool Roles
@ -144,6 +146,26 @@ Once design tokens exist (step 9), use token names:
This builds shared vocabulary. Over time, the user learns to say "change from space-md to space-lg" instead of "add more space."
### Pattern recognition — reflect, don't interrogate
When the user requests a spacing adjustment, the agent's job is to **observe and reflect** — not to ask "why?" A trained designer carries spacing patterns unconsciously. Their gut says "more space here" because a pattern is firing in the back of their brain. The agent externalizes that intuition.
**Wrong:** "Why does this need more space?" — breaks the flow, puts the meta-work on the designer.
**Right:** "Got it — large image above a card row needs extra breathing room. I'll use space-xl + space-xs for this relationship going forward."
The designer nods or corrects. The agent records it. The pattern table in the design system builds itself as a byproduct of doing the work.
**The process:**
1. User says "more space between the photo and the cards"
2. Agent fixes it: `space-lg + space-xs`
3. Agent reflects: "So when an image-with-text block sits above a card row, the default gap isn't enough."
4. First time: one-off adjustment noted in the page spec
5. Second time: agent says "this is the same pattern as the homepage about section — applying it"
6. Third time: agent extracts it to `D-Design-System/00-design-system.md` → Patterns
This is how a designer's unconscious expertise becomes a shared, reusable asset. The agent does the tedious classification and recall work. The designer just keeps designing.
---
## When to Use This Loop

View File

@ -152,6 +152,38 @@
| EN | "{English text}" |
| Behavior | {onClick / onChange / etc.} |
#### ↕ `{page-name}-{above}-{below}-gap` — {spacing token}
<!--
Spacing objects sit between content objects. They have IDs and are first-class.
RULES:
- Default element gap (from the Spacing section above) is implicit — no spacing object needed.
- Non-default spacing MUST be an explicit spacing object with an ID.
- Zero spacing (overlap / flush) MUST be documented: ↕ `id` — space-zero (reason)
- Spacing objects flow into D-Design-System/00-design-system.md → Patterns.
FORMAT: #### ↕ `{id}` — {token} [{optional reason}]
EXAMPLES:
#### ↕ `hem-heading-subtitle-gap` — space-sm
#### ↕ `hem-icons-about-gap` — space-zero (icon bar sits flush against section below)
#### ↕ `hem-about-trust-gap` — space-xl + space-xs (busy content needs breathing room)
-->
---
#### {Object Name 2}
**OBJECT ID:** `{page-name}-{object-name-2}`
| Property | Value |
|----------|-------|
| Component | [{Component}]({path}) |
| Translation Key | `{translation.key}` |
| SE | "{Swedish text}" |
| EN | "{English text}" |
---
#### {Group Name} (Container)
@ -175,6 +207,8 @@
| SE | "{Swedish text}" |
| EN | "{English text}" |
##### ↕ `{page-name}-{group-name}-{obj1}-{obj2}-gap` — {spacing token}
##### {Object in Group 2}
**OBJECT ID:** `{page-name}-{group-name}-{object-2}`