Skip to content

07 — Design Tokens

“Wijzig je een brand color → alles downstream past zich automatisch aan. Één bron, oneindige consistentie.”

Het design token systeem is de technische backbone van Beam’s visuele identiteit. Alles wat er visueel gebeurt — de editor, blocks, addons, AI-gegenereerde content, exports — leest uit één bron: het token schema.


  1. Theming Cascade
  2. Drie-Laags Token Architectuur
  3. Style Pack ↔ Token Schema Alignment
  4. Guardrail Systeem
  5. Semantic Context Laag
  6. Volledig Token Schema
  7. Publieke Token API
  8. Storage Structuur
  9. Export Formaten
  10. Technische Beslissingen
  11. Backlog

Tokens cascaderen van laag naar hoog. Hogere lagen overschrijven lagere.

Laag 5 │ User Overrides (styling builder: handmatige aanpassingen)
│ Altijd de hoogste prioriteit — de gebruiker wint
Laag 4 │ Vertical Pack Styling
│ Niche-specifieke overrides (Yoga Studio's warme radius)
Laag 3 │ Style Packs (gilde-gebaseerd)
│ "Healer Calm", "Creator Bold", "Merchant Trust"
│ = het startpunt dat de styling builder aanbiedt
Laag 2 │ Addon Global Styles
│ CSS variabelen, fonts die addons meebrengen
Laag 1 │ Beam Core Tokens
│ Platform defaults — de basis die altijd geldt
────────┤
Tokens │ W3C Design Token Schema (single source of truth)
│ Alles hierboven resolved naar dit schema

Cascade-regel: Laag 1 en 2 zijn read-only vanuit de styling builder. De builder bewerkt Laag 3 (Style Pack keuze), Laag 4 (vertical pack overrides) en Laag 5 (user overrides).


Brand Tokens → Semantic Tokens → Element Tokens
(ruwe waarden) (betekenis/rol) (toepassing)
#1B4D7A → primary color → button background
Playfair Display → heading font → h1, h2, h3
6px → base radius → card corner

Tokens werken altijd via refs — nooit hardcoded waarden. Wijzig je een brand color → alles downstream past zich automatisch aan via OKLCH shade recalculatie.


De styling builder is het bewerkoppervlak, het token schema is de opslag, en de Style Pack is het distributieformaat voor het addon systeem. Dezelfde data, drie representaties.

StylePack (addon systeem) Token Schema (styling builder)
───────────────────────── ────────────────────────────────
colors.brand → tokens.colors.brand.primary
colors.primary → tokens.colors.semantic.primary
colors.accent → tokens.colors.brand.accent
colors.surface → tokens.colors.semantic.surface
colors.text → tokens.colors.semantic.text
colors.muted → tokens.colors.semantic.text-muted
fonts.heading → tokens.typography.fonts.heading
fonts.body → tokens.typography.fonts.body
borderRadius → tokens.shape.radius.preset
spacing → tokens.shape.spacing.preset
componentStyles → elements.* (button, table, etc.)
seasonal.overrides → meta.seasonal_overrides

“You cannot make it ugly” is geen aspiratie — het is een technische implementatie. Guardrails zijn altijd actief, niet optioneel.

RegelImplementatieFeedback
Max 3 brand colorsUI blokkeert 4e kleur”Drie kleuren zijn genoeg voor een sterk merk.”
WCAG AA contrastReal-time check bij elke kleurwijzigingContrast ratio badge: ✅ AA / ⚠️ Bijna / ❌ Onvoldoende
Harmonie checkKleuren buiten OKLCH-gegenereerde schaal worden gewaarschuwd”Deze kleur botst met je palette. Probeer deze suggestie.”
Dark mode contrastAutomatisch gevalideerd bij generatieIdem
RegelImplementatieFeedback
Curated font-parenAlleen vooraf goedgekeurde combinaties heading+bodyFont picker toont compatibele matches
Geen extreme sizesMin/max per element type (H1: 1.5rem–4rem)Slider stopt bij grenzen
Line-height validatieAutomatisch berekend op basis van font sizeNiet handmatig instelbaar tenzij “geavanceerd”
Mobile sizesAutomatisch afgeleid uit desktop (ratio)Preview toont mobiel altijd mee
RegelImplementatieFeedback
Consistente radiusEén radius-preset voor alles, sm/base/lg zijn afgeleidGeen losse radius per element
Spacing systeemBase unit × vermenigvuldigers, geen vrije waardenSpacing slider met stappen
Shadow coherentieDrie niveaus (sm/md/lg), niet vrij instelbaarShadow preset selector

Guardrails voelen niet als restrictie maar als hulp:

SituatieVisueelAudio
Kleur voldoet aan WCAG AASubtiel groen vinkjeZacht “ding”
Kleur faalt contrastKleur schudt licht + suggestie slide-inZacht “bonk”
Font-paar is harmonieusFonts schuiven soepel in positie
Radius verandertAlle elementen animeren smooth mee (spring physics)Subtiel “click” per stap
Kleurwijziging propageertRipple-animatie door alle gerelateerde tokens in sidebarToon (pitch volgt hue)

Tokens zijn contextbewust. Een primary kleur op een donkere achtergrond gebruikt automatisch een lichtere variant:

"primary": {
"default": { "ref": "brand.primary" },
"on-dark": { "ref": "tokens.colors.generated.primary-300" },
"on-light": { "ref": "tokens.colors.generated.primary-700" }
}

Beam lost dit automatisch op op basis van de achtergrondkleur van de container — onzichtbaar voor de gebruiker, ingebakken in het schema.


{
"brand": {
"name": "Bouwbedrijf De Vries",
"logo": {
"url": "https://r2.beam.app/sites/abc123/brand/logo.svg",
"favicon": null
}
},
"tokens": {
"colors": {
"brand": {
"primary": "#1B4D7A",
"secondary": "#F5F0EB",
"accent": "#D4883E"
},
"semantic": {
"primary": {
"default": { "ref": "brand.primary" },
"on-dark": { "ref": "tokens.colors.generated.primary-300" },
"on-light": { "ref": "tokens.colors.generated.primary-700" },
"dark-mode-override": null
},
"background": { "value": "#FFFFFF", "dark-mode-override": "#0F172A" },
"surface": { "value": "#F8F9FA", "dark-mode-override": "#1E293B" },
"text": { "value": "#1A1A2E", "dark-mode-override": "#F1F5F9" },
"text-muted": { "value": "#6B7280", "dark-mode-override": "#94A3B8" },
"border": { "value": "#DEE2E6", "dark-mode-override": "#334155" },
"danger": { "value": "#C92A2A" },
"success": { "value": "#2F9E44" },
"warning": { "value": "#E67700" }
},
"generated": {
"primary-50": "auto (OKLCH)",
"primary-100": "auto (OKLCH)",
"primary-200": "auto (OKLCH)",
"primary-300": "auto (OKLCH)",
"primary-400": "auto (OKLCH)",
"primary-500": "auto (OKLCH)",
"primary-600": "auto (OKLCH)",
"primary-700": "auto (OKLCH)",
"primary-800": "auto (OKLCH)",
"primary-900": "auto (OKLCH)"
}
},
"typography": {
"fonts": {
"heading": { "family": "Playfair Display", "source": "google" },
"body": { "family": "Lato", "source": "google" },
"mono": { "family": "JetBrains Mono", "source": "google" }
},
"scale": {
"h1": { "font": "heading", "size": { "desktop": "3rem", "mobile": "2.25rem" }, "weight": 700, "lineHeight": 1.2 },
"h2": { "font": "heading", "size": { "desktop": "2.25rem", "mobile": "1.875rem" }, "weight": 600, "lineHeight": 1.3 },
"h3": { "font": "heading", "size": { "desktop": "1.875rem", "mobile": "1.5rem" }, "weight": 600, "lineHeight": 1.3 },
"h4": { "font": "heading", "size": { "desktop": "1.5rem", "mobile": "1.25rem" }, "weight": 500, "lineHeight": 1.4 },
"h5": { "font": "heading", "size": { "desktop": "1.25rem", "mobile": "1.125rem" }, "weight": 500, "lineHeight": 1.4 },
"h6": { "font": "heading", "size": { "desktop": "1rem", "mobile": "1rem" }, "weight": 500, "lineHeight": 1.4 },
"body": { "font": "body", "size": { "desktop": "1rem", "mobile": "1rem" }, "weight": 400, "lineHeight": 1.6 },
"label": { "font": "body", "size": { "desktop": "0.875rem", "mobile": "0.875rem" }, "weight": 500, "lineHeight": 1.4 },
"caption": { "font": "body", "size": { "desktop": "0.75rem", "mobile": "0.75rem" }, "weight": 400, "lineHeight": 1.4 }
}
},
"shape": {
"radius": {
"preset": "balanced",
"sm": "4px",
"base": "6px",
"lg": "10px",
"pill": "9999px"
},
"spacing": { "preset": "normal", "base": "4px" },
"shadow": {
"preset": "subtle",
"sm": "0 1px 2px rgba(0,0,0,0.05)",
"md": "0 4px 6px rgba(0,0,0,0.07)",
"lg": "0 10px 15px rgba(0,0,0,0.1)"
}
}
},
"elements": {
"button": {
"primary": {
"bg": { "ref": "semantic.primary.default" },
"text": { "value": "#FFFFFF" },
"border": { "value": "transparent" },
"radius": { "ref": "shape.radius.base" },
"paddingX": "1.25rem",
"paddingY": "0.625rem",
"font": { "ref": "typography.scale.label" },
"states": {
"hover": { "bg": { "ref": "tokens.colors.generated.primary-700" } },
"focus": { "ring": { "ref": "semantic.primary.default" }, "ringOffset": "2px" },
"disabled": { "opacity": 0.5 },
"loading": { "opacity": 0.8 }
}
},
"secondary": {
"bg": { "ref": "semantic.surface" },
"text": { "ref": "semantic.primary.default" },
"border": { "ref": "semantic.border" },
"radius": { "ref": "shape.radius.base" },
"states": {
"hover": { "bg": { "ref": "tokens.colors.generated.primary-50" } }
}
},
"ghost": {
"bg": { "value": "transparent" },
"text": { "ref": "semantic.primary.default" }
},
"destructive": {
"bg": { "ref": "semantic.danger" },
"text": { "value": "#FFFFFF" }
}
},
"table": {
"headerBg": { "ref": "semantic.surface" },
"borderColor": { "ref": "semantic.border" },
"stripeColor": { "ref": "tokens.colors.generated.primary-50" },
"cellPadding": "0.75rem 1rem"
},
"list": {
"markerColor": { "ref": "semantic.primary.default" },
"itemSpacing": "0.375rem",
"indent": "1.5rem"
},
"input": {
"border": { "ref": "semantic.border" },
"radius": { "ref": "shape.radius.base" },
"focusRing": { "ref": "semantic.primary.default" },
"bg": { "value": "#FFFFFF" }
},
"badge": {
"radius": { "ref": "shape.radius.pill" },
"font": { "ref": "typography.scale.caption" }
},
"codexCard": {
"bg": { "ref": "semantic.surface" },
"border": { "ref": "semantic.border" },
"radius": { "ref": "shape.radius.lg" },
"titleFont": { "ref": "typography.scale.label" },
"rarityGlow": { "ref": "semantic.primary.default" }
}
},
"meta": {
"version": "2.0",
"onboarding_completed_at": "2026-02-14T12:00:00Z",
"last_published_at": null,
"source_mood": "professional-warm",
"source_font_preference": "classic-serif",
"source_gilde": "healer",
"source_class": "guide",
"style_pack_id": "healer-calm",
"token_format": "w3c-design-tokens-draft",
"seasonal_overrides": null,
"guardrails": {
"contrast_aa_pass": true,
"color_harmony_pass": true,
"font_pair_validated": true,
"last_checked_at": "2026-02-14T12:00:00Z"
}
}
}

Elk Beam-merk heeft een read-only token endpoint. De pagebuilder, AI content generator, addon blocks, Codex kaart-rendering en externe tools lezen hier live uit.

GET /api/sites/{site_id}/tokens
→ Volledig token schema in W3C formaat
GET /api/sites/{site_id}/tokens/css
→ CSS variables, kant-en-klaar voor embedden
GET /api/sites/{site_id}/tokens/resolved
→ Alle refs opgelost, cascade toegepast

Tokens zijn de bron — CSS, Tailwind en W3C JSON zijn afgeleiden.


CF R2: beam-media/
sites/
{site_id}/
media/ ← afbeeldingen (onboarding + media library)
fonts/ ← eigen WOFF2 uploads
brand/ ← logo, favicon

Noot over fonts: Curated Google Fonts worden geladen via de Google Fonts API. Eigen WOFF2 uploads worden opgeslagen in R2 (sites/{site_id}/fonts/).


Alle exports zijn afgeleiden van het token schema.

1. CSS Variables

:root {
--color-primary: #1B4D7A;
--color-primary-50: /* OKLCH gegenereerd */;
--font-heading: 'Playfair Display', serif;
--radius-base: 6px;
}

2. Tailwind v4 (@theme)

@theme {
--color-primary: #1B4D7A;
--font-heading: 'Playfair Display';
--radius-base: 6px;
}

3. W3C Design Tokens JSON Primair formaat. Compatibel met Figma Tokens, Style Dictionary, Canva (via Figma bridge).

4. Style Pack formaat Export als StylePack interface voor het addon systeem:

{
id: 'custom-my-brand',
name: 'Mijn Merk',
gilde: 'healer',
colors: { brand: '#1B4D7A', primary: '#1B4D7A', accent: '#D4883E', ... },
fonts: { heading: { family: 'Playfair Display', ... }, body: { ... } },
borderRadius: 'medium',
spacing: 'comfortable',
componentStyles: { /* resolved element tokens */ }
}

OnderwerpBeslissing
Color shade algoritmeOKLCH via culori library
Dark modeAuto-generatie met handmatige override per token
Font bronnenCurated subset Google Fonts + eigen WOFF2 upload → R2
Tailwind exportv4 CSS-first via @theme
Token formaatW3C Design Token spec als primair formaat
PublicerenDraft staat met aparte publiceer stap
VersioningAuto-save + named snapshots
Responsive typografieVaste sizes per breakpoint (desktop + mobiel apart)
Component statesHover + focus handmatig, disabled + loading automatisch afgeleid
ScopePer site — geen multi-site (agency feature: backlog B9)
Theming cascadeLaag 1-5 systeem: Core → Addon → Style Pack → Vertical → User
GuardrailsAltijd actief, niet optioneel — kern-feature
Style Pack formaatBi-directioneel: StylePack ↔ Token Schema
Seasonal overridesOndersteuning voor tijdgebonden token-aanpassingen
Gilde-awarenessBuilder kent het gilde en past suggesties aan

#FeatureOmschrijving
B1Brand health scoreLive indicator hoe consistent tokens worden toegepast. Hardcoded waarden buiten token systeem verlagen de score.
B2Token importW3C JSON importeren vanuit Figma Tokens, Style Dictionary of andere Beam site
B3”Gebruikt in” impact previewToon waar een token gebruikt wordt vóór je het wijzigt. “Deze kleur zit in 3 pagina’s, 2 buttons, 1 tabel.”
#FeatureOmschrijving
B4Brand lock / permissionsAgency vergrendelt primaire kleur + logo, klant past de rest aan.
B5Changelog / audit trailLeesbare geschiedenis: “Angelo heeft op 14 feb de primary color gewijzigd.”
B6AI stijlgeneratie vanuit URLVoer een inspiratie URL in → Beam extraheert stijl als token startpunt.
B7Animaties & transitionsGlobale instelling: transition snelheid + easing preset.
B8Fluid typographyclamp() per breakpoint als alternatief voor vaste sizes.
#FeatureOmschrijving
B9Agency / multi-site templatesGedeelde token templates over meerdere sites.
B10Real-time samenwerkingMeerdere gebruikers tegelijk in de styling builder.
B11Tokens overdraagbaar naar andere platformsE-mail templates, Canva social media, pitch decks.
B12AI als merkbewakerAI toetst elke nieuwe pagina aan de merkidentiteit.
B13Semantische betekenislaagTokens beschrijven niet alleen hoe iets eruitziet maar wat het betekent. “Blauw = vertrouwen.”