Token Reference v4
The v4 catalogue. Brand modes move from Primitives onto Semantic. Primitives flattens into a brand-prefixed palette library with no modes. Theme, Regulatory, and Component keep the shape they have in v3.1. This document is structural — it enumerates the changes, not every single token. Full token enumeration follows the brand-collapse audit.
How to read v4
Each section belongs to one collection. Status badges tell you what changed from v3.1:
new — new in v4 (or structurally new: brand modes on Semantic, flat naming on Primitives).
changed — token kept, but naming or mode placement is different from v3.1.
unchanged — carried forward from v3.1 byte-for-byte.
Reading the alias notation
Throughout the document, → means "aliases to." Example:
Semantic.interactive/primary/bg (mode: snabbare) → Primitives.color/snabbare/primary-600 = #1e8f3e
v3.1 → v4 · the structural diff
Two collections change. Three stay identical.
| Collection | v3.1 (shipped) | v4 (proposed) | Status |
|---|---|---|---|
| 1 · Primitives | 20 brand modes, ~110 tokens each | unmoded, flat names, ~990 tokens | changed |
| 2 · Theme | 2 modes (light/dark), ~28 tokens | same | unchanged |
| 3 · Semantic | no modes, ~340 tokens | 9–20 brand modes, ~340 tokens per mode | brand modes |
| 4 · Regulatory | 7 jurisdiction modes, ~22 tokens | same | unchanged |
| 5 · Component | no modes, ~35 alias packs | same | unchanged |
The invariant v4 establishes
Each mode axis lives in exactly one collection. Brand on Semantic. Theme on Theme. Jurisdiction on Regulatory. There is no cross-collection name-matching; all aliases point at explicit targets.
1 · Primitives changed
Naming shape
# Colour ramps — fully-qualified per brand color/lyllo/primary-{50,100,200,...,950} color/lyllo/secondary-{50..950} color/lyllo/tertiary-{50..950} color/snabbare/primary-{50..950} color/comeon/primary-{50..950} # ... same shape for each brand # Shared neutrals — not brand-specific color/neutral-{0,50,100,...,950,1000} # Shared status — not brand-specific color/status/success-{50..950} color/status/warning-{50..950} color/status/error-{50..950} color/status/info-{50..950} # Chrome — per-brand where brands need a specific chrome color/lyllo/header-chrome # = #ffffff color/snabbare/header-chrome # = #0b5ed7 (isolated from brand primary) color/comeon/header-chrome # = brand-primary tone # ... per brand
Brand ramps (one per brand)
Each audited brand gets a complete set of ramps under color/<brand>/*. The ramp step count (9 steps: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950) is preserved from v3.1 — no ramp steps are trimmed.
lyllo
color/lyllo/primary-{50..950}color/lyllo/secondary-{50..950}color/lyllo/tertiary-{50..950}
snabbare
color/snabbare/primary-{50..950}color/snabbare/header-chrome
comeon
color/comeon/primary-{50..950}
mobilebet
color/mobilebet/primary-{50..950}
mobilespin
color/mobilespin/primary-{50..950}color/mobilespin/cta-yellow
getlucky
color/getlucky/primary-{50..950}color/getlucky/tag-blue
reviant-a
color/reviant-a/primary-{50..950}
reviant-b
color/reviant-b/primary-{50..950}
888.nl
color/888nl/primary-{50..950}color/888nl/header-chrome
Brand-collapse candidates
Before rename, the brand-collapse audit identifies brands that may be ≥95% Lyllo-identical with only accent overrides. Candidates: reviant-a, reviant-b, 888.nl. If any collapses, its Primitives shrink to only the 3–8 accent tokens that differ — the rest inherits via Semantic's sub-brand mode.
Shared tokens (not per-brand)
Tokens that don't vary across brands live under flat, non-brand-prefixed names:
color/neutral-{0,50,100,200,300,400,500,600,700,800,900,950,1000} color/status/success-{50..950} color/status/warning-{50..950} color/status/error-{50..950} color/status/info-{50..950} color/always/white # = #ffffff color/always/black # = #000000
Chrome (per-brand where needed)
Chrome tokens that were color/chrome/* in v3.1 (added in the 2026-04-21 Theme rewire) keep their role but become brand-prefixed where they vary:
color/lyllo/header-chrome color/snabbare/header-chrome color/comeon/header-chrome # ... per brand # Universal chrome stays under shared namespace color/neutral/scrim color/neutral/divider
Typography, radius, layout
Typography families, radius scale, and layout tokens remain per-brand where they differ (e.g. typography/lyllo/family-display) and shared otherwise (radius/scale-4, space/4). Identical shape to v3.1, just with the brand prefix flattened into the name.
2 · Theme unchanged
color/neutral-950) instead of moded ones.Surface
| Token | mode: light | mode: dark |
|---|---|---|
| surface/page | → color/neutral-50 | → color/neutral-950 |
| surface/raised | → color/neutral-0 | → color/neutral-900 |
| surface/elevated | → color/neutral-0 | → color/neutral-800 |
| surface/sunken | → color/neutral-100 | → color/neutral-950 |
| surface/inverse | → color/neutral-950 | → color/neutral-50 |
| surface/skeleton | → color/neutral-100 | → color/neutral-800 |
| surface/skeleton-shimmer | → color/neutral-200 | → color/neutral-700 |
| surface/tooltip | → color/neutral-900 | → color/neutral-100 |
| surface/code | → color/neutral-50 | → color/neutral-900 |
Text & border
| Token | mode: light | mode: dark |
|---|---|---|
| text/primary | → color/neutral-950 | → color/neutral-50 |
| text/secondary | → color/neutral-700 | → color/neutral-200 |
| text/muted | → color/neutral-500 | → color/neutral-400 |
| text/disabled | → color/neutral-300 | → color/neutral-600 |
| text/inverse | → color/neutral-50 | → color/neutral-950 |
| text/placeholder | → color/neutral-400 | → color/neutral-500 |
| border/default | → color/neutral-200 | → color/neutral-800 |
| border/subtle | → color/neutral-100 | → color/neutral-900 |
| border/strong | → color/neutral-400 | → color/neutral-600 |
| overlay/backdrop-opacity | 0.4 | 0.6 |
Note on Theme's relationship to brand
Theme is brand-agnostic. It flips chrome between light and dark using neutrals only. Brand colouring happens in Semantic (per brand mode), which aliases Theme for chrome bits and Primitives for brand-coloured bits. Theme never needs to know the current brand.
3 · Semantic brand modes
The mode table at a glance
Below, each row is a Semantic token. Columns show its alias under three representative brand modes (lyllo · snabbare · comeon). Italic rows are brand-identical. Coloured rows are brand-divergent.
Interactive · diverges per brand
| Semantic token | mode: lyllo | mode: snabbare | mode: comeon |
|---|---|---|---|
| interactive/primary/bg | → color/lyllo/primary-600 | → color/snabbare/primary-600 | → color/comeon/primary-600 |
| interactive/primary/bg-hover | → color/lyllo/primary-700 | → color/snabbare/primary-700 | → color/comeon/primary-700 |
| interactive/primary/bg-pressed | → color/lyllo/primary-800 | → color/snabbare/primary-800 | → color/comeon/primary-800 |
| interactive/primary/fg | → color/always/white | → color/always/white | → color/always/black |
| interactive/secondary/bg | → color/lyllo/secondary-500 | → Theme.surface/raised | → Theme.surface/raised |
| interactive/accent/bg | → color/lyllo/tertiary-500 | → color/snabbare/primary-500 | → color/comeon/primary-400 |
Text · mostly brand-identical
| Semantic token | mode: lyllo | mode: snabbare | mode: comeon |
|---|---|---|---|
| text/primary | → Theme.text/primary | → Theme.text/primary | → Theme.text/primary |
| text/secondary | → Theme.text/secondary | → Theme.text/secondary | → Theme.text/secondary |
| text/brand | → color/lyllo/primary-600 | → color/snabbare/primary-600 | → color/comeon/primary-600 |
| text/on-brand | → color/always/white | → color/always/white | → color/always/black |
| text/link | → color/lyllo/primary-700 | → color/snabbare/primary-700 | → color/comeon/primary-700 |
Icon · partially diverges
| Semantic token | mode: lyllo | mode: snabbare | mode: comeon |
|---|---|---|---|
| icon/default | → Theme.text/primary | → Theme.text/primary | → Theme.text/primary |
| icon/action | → color/lyllo/primary-600 | → color/snabbare/primary-600 | → color/comeon/primary-600 |
| icon/chevron | → color/neutral-400 | → color/neutral-400 | → color/neutral-400 |
| icon/big | → color/lyllo/secondary-500 | → color/snabbare/primary-500 | → color/comeon/primary-500 |
| icon/descriptive | → color/lyllo/tertiary-400 | → color/snabbare/primary-400 | → color/comeon/primary-400 |
| icon/nav-selected | → color/lyllo/primary-600 | → color/snabbare/primary-600 | → color/comeon/primary-600 |
Pill & badge · diverges
| Semantic token | mode: lyllo | mode: snabbare | mode: comeon |
|---|---|---|---|
| pill/active/bg | → color/lyllo/primary-600 | → color/snabbare/primary-600 | → color/comeon/primary-600 |
| pill/inactive/bg | → Theme.surface/raised | → Theme.surface/raised | → Theme.surface/raised |
| badge/accent/bg | → color/lyllo/tertiary-500 | → color/snabbare/primary-500 | → color/comeon/primary-500 |
| badge/live/bg | → color/status/error-500 | → color/status/error-500 | → color/status/error-500 |
Radius · structural (mostly brand-identical)
| Semantic token | mode: lyllo | mode: snabbare | mode: reviant-a |
|---|---|---|---|
| radius/interactive | → radius/scale-8 | → radius/scale-8 | → radius/scale-12 |
| radius/pill | → radius/scale-full | → radius/scale-full | → radius/scale-0 |
| radius/card | → radius/scale-12 | → radius/scale-12 | → radius/scale-12 |
| radius/modal | → radius/scale-16 | → radius/scale-16 | → radius/scale-16 |
| radius/badge | → radius/scale-4 | → radius/scale-4 | → radius/scale-4 |
Rough split: what's brand-identical vs. brand-divergent
Across all ~340 Semantic tokens:
- ~290 brand-identical — same alias across every mode (chrome, neutrals, status, typography, most radii). Copy-paste per mode.
- ~50 brand-divergent — different alias per mode (interactive, accent, brand-coloured icons, pill active, badge accent, text-on-brand).
Sub-brands (from brand-collapse audit) override only ~3–8 of the 50 divergent tokens; the rest inherits from the parent brand.
4 · Regulatory unchanged
rg/*), bonus terms (bonus/*), and age gate (age-gate/*). Carries forward from v3.1 byte-for-byte.Regulatory sits beside Semantic as a narrow override layer — it aliases Primitives directly for a small set of compliance-related tokens that must vary per jurisdiction regardless of brand or theme. Devs consume these only in compliance contexts (rg-limit-display, bonus-terms-footer, age-gate-cover).
Why Regulatory stays separate: the jurisdiction axis is independent of brand and theme. SE rules apply regardless of which brand the user is on. Moving it onto Semantic would force a 7× multiplier on an already 9-mode collection for zero UX gain.
Token families (unchanged from v3.1)
rg/limit-display/*— deposit/loss/session limit displaysrg/self-exclusion/*— exclusion banners and flowsrg/reality-check/*— reality-check modal surfacesbonus/terms/*— wagering T&Cs typography and emphasisbonus/disclaimer/*— fine-print colours per jurisdictionage-gate/cover/*— age-gate splash colours
5 · Component unchanged
cta/*, header/*, nav/*, menu-row/*, view-all/*. Marketing cannot edit. Inherits brand variation through Semantic without participating in modes.Component's one rule: aliases only Semantic. Never reaches around to Primitives directly. Never touches Theme directly. Because Semantic resolves brand per mode, Component inherits brand-correct values automatically.
Three worked examples
Example 1 · CTA primary
Component.cta/primary/bg → Semantic.interactive/primary/bg (brand-dependent) # When Semantic.mode = snabbare: → Primitives.color/snabbare/primary-600 = #1e8f3e # When Semantic.mode = lyllo: → Primitives.color/lyllo/primary-600 = #ff6699
Example 2 · Snabbare's isolated header
Component.header/bg → Semantic.chrome/header/bg (brand-dependent) # When Semantic.mode = snabbare: → Primitives.color/snabbare/header-chrome = #0b5ed7 # When Semantic.mode = lyllo: → Primitives.color/lyllo/header-chrome = #ffffff
Example 3 · Menu-row icon split
Component.menu-row/action-icon → Semantic.icon/action (brand-dependent) Component.menu-row/chevron → Semantic.icon/chevron (brand-identical, neutral)
Summary of Component's unchanged packs
cta/primary/*,cta/form/*,cta/wallet/*,cta/landing/*— CTA variants with different radii per contextheader/*— isolated header chromemenu-row/*— action/chevron splitnav/selected-*— navigation selected stateview-all/*— label/icon split
Migration · v3.1 → v4 token map
First 20 high-traffic tokens and their v4 equivalents. The full map covers ~340 Semantic tokens × 9 brand modes.
| v3.1 token (current) | v4 equivalent | Change |
|---|---|---|
| color/primary-600 [mode=snabbare] (Primitives) | color/snabbare/primary-600 (Primitives, flat) | renamed, unmoded |
| color/primary-600 [mode=lyllo] | color/lyllo/primary-600 | renamed, unmoded |
| color/primary-600 [mode=comeon] | color/comeon/primary-600 | renamed, unmoded |
| color/chrome/header-snabbare (Primitives) | color/snabbare/header-chrome | name normalised, brand prefix moved to front |
| color/neutral-50 (Primitives) | color/neutral-50 | unchanged (not brand-specific) |
| interactive/primary/bg (Semantic, no modes) | interactive/primary/bg (Semantic, 9–20 brand modes) | same name, now moded |
| text/primary (Semantic, no modes) | text/primary (Semantic, mode=*, all aliases → Theme.text/primary) | brand-identical across modes |
| icon/action (Semantic) | icon/action (Semantic, per-brand) | brand-divergent, now moded |
| icon/chevron (Semantic) | icon/chevron (Semantic, brand-identical) | same alias in every mode |
| pill/active/bg (Semantic) | pill/active/bg (Semantic, per-brand) | brand-divergent, now moded |
| radius/pill (Semantic) | radius/pill (Semantic, per-brand) | brand-divergent for reviant-b (r=0) |
| surface/page (Theme) | surface/page (Theme) | unchanged |
| border/default (Theme) | border/default (Theme) | unchanged |
| rg/limit-display/bg (Regulatory) | rg/limit-display/bg (Regulatory) | unchanged |
| cta/primary/bg (Component) | cta/primary/bg (Component) | unchanged bind; brand resolved via Semantic mode |
| header/bg (Component) | header/bg (Component) | unchanged |
| menu-row/action-icon (Component) | menu-row/action-icon (Component) | unchanged |
| nav/selected-bg (Component) | nav/selected-bg (Component) | unchanged |
| view-all/label (Component) | view-all/label (Component) | unchanged |
| cta/form/radius (Component) | cta/form/radius (Component) | unchanged |
Migration phases
- Brand-collapse audit — read-only against brand-audit.html. Flag sub-brands.
- Flatten Primitives — start with Snabbare (99.5% hex parity). Rename moded → flat. Verify byte-for-byte.
- Add Semantic brand modes —
lyllo+snabbarefirst. Bind ~340 aliases per mode. - Style Dictionary pipeline — emit
dist/tokens/<brand>-<theme>.css. - Swap prototype — Global Search Casino off hand-written
theme.cssto generated bundles atlocalhost:5180. - Scale & cut-over — all 9 active brands × 2 themes = 18 bundles. Archive v3.1 Figma as read-only reference.