20-Brand White Label · ComeOn · 2026-04-30 · Final

Design Token
Architecture v3.2

The final architecture for ComeOn's white-label platform. Six collections. Two locked invariants. Nine brand modes. Light + Dark theme. Brand DNA in three ramps plus a tertiary accent. Implemented in Figma file fysBbW10LRJUNgisaBKXJo on 2026-04-29.

FINAL · 2026-04-30 6 collections 9 brand modes Light + Dark ~382 tokens marketing edits Modes only

Token Reference v3.2 Component Audit Brand Audit (evidence)

v3.2 supersedes v3.1 and v4. The v4 exploration is preserved in the Archive; it was dropped on 2026-04-29.

02Why v3.2 is final

v4 was an architecture; v3.2 is a discipline.

v4 proposed flattening Primitives into a brand-prefixed library and moving the brand mode axis onto Semantic. After three iteration sessions the team concluded the migration cost outweighed the structural win — and that the real problem was not the shape of the collections but the discipline applied inside them. v3.2 keeps the v3.1 collection model and locks two rules that make it stay clean.

Why v4 was dropped

  • Flattening 20 brand modes to ~990 flat tokens lost the single-edit-point per ramp (marketing now has to touch 9 places to retint a ramp).
  • Moving brand mode to Semantic broke the orthogonality between brand and theme — the .dark toggle stopped being a single-axis flip.
  • Library subscribers (Lyllo Casino, Brand Tokens, etc.) would each need their own v4 adoption; without that, Semantic would alias into stale Primitives.
  • The migration generated a measurable amount of dormant _legacy/* binding cruft that took two cleanup sessions to retire.

What v3.2 locks down

  • Hex invariant — raw hex lives in exactly two homes: Global (system + black/white/alpha) and Modes brand DNA (primary, secondary, neutral, tertiary).
  • Strict 4-point scaleGlobal/scale/* contains only [0, 4, 8, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 72, 80, 96, 112, 128, 144, 160]. No off-grid stops.
  • Tertiary ramp in brand DNA for accent colours that don't fit primary/secondary (Lyllo orange, Comeon lime).
  • Semantic spacing + icon-size exposed for component consumption.
  • Theme is alias-only — every Light + Dark resolution chains through Modes DNA.
03Six collections

The shape of v3.2.

Each axis lives in exactly one collection. Aliases flow downward only: Global is the floor, Component Packs is the ceiling.

Global · 1 mode (Default) · 116 vars + 5 elevation Effect Styles
raw values
Modes · 9 brand modes · 140 vars (DNA + alias chrome)
brand axis
Theme · 2 modes (Light, Dark) · 31 vars · 100% alias
theme axis
Semantic · 1 mode · 53 vars · all aliases
role layer
Component Packs · 1 mode · 38 vars · all aliases
component layer
Regulatory · 7 modes · 4 vars
jurisdiction axis

Total: ~382 active tokens across ~2,180 distinct values when unrolled across all modes. Down from v3.1's ~525 because the chrome layer collapsed when Modes absorbed it.

04Alias chain

How a token resolves at the component.

Components bind Component Packs or Semantic. Each layer aliases the layer below. Frame mode = brand × theme. The chain ends at Global or Modes DNA.

# Frame mode: brand=Lyllo, theme=Light

Component Packs/cta/primary/background
  → Semantic/color/brand/primary
    → Modes/color/brand/primary # resolves per brand mode
      → Modes/color/primary/500 # Lyllo: #F02884 · raw hex (DNA)

Component Packs/cta/primary/text
  → Theme/color/text/on-brand
    → Modes/color/text/on-brand # Light: aliases Global/color/white
      → Global/color/white # #FFFFFF · raw hex

Component Packs/cta/radius
  → Semantic/radius/sm
    → Modes/radius/cta
      → Global/scale/4 # 4px (rounded from legacy 3)

Component Packs/cta/padding/x
  → Semantic/spacing/md
    → Global/scale/16 # 16px

Read this as: component → role → brand-or-theme → DNA-or-Global. Switching Theme=Dark at the frame swaps only the Theme leg; switching Modes=Snabarre swaps the Modes leg. Global is invariant across everything.

05Collection 1 · Global

The floor: brand-invariant raw values.

116 variables + 5 elevation Effect Styles. Single Default mode. Holds black, white, system colours, the 4-point scale, typography sizes, breakpoints, strokes — anything that does not change per brand or theme.

Global collection groups
GroupSizesNotes
color/black, /white2Pure raw hex
color/black-alpha/{0..90}10Overlay alphas
color/white-alpha/{0..90}10Overlay alphas
color/system/{success, warning, error, info}/50-90040Brand-invariant feedback
scale/{0,4,8,…,160}20Strict 4-point only
typography/size/* + line-height/*~16Aliases scale
typography/letter-spacing/{tight,normal,wide}3Raw FLOAT
stroke/{hairline,default,thick,strong}4Aliases scale
breakpoint/{mobile,tablet,desktop,desktop-wide}4FLOAT
elevation/0..45Effect Styles

Why Global is small

Anything that could vary per brand goes into Modes instead. Anything that must stay the same across every brand and every theme — like color/white or scale/16 — lives here. The size of Global is the rate at which we add new universal foundations, which should be near zero in steady state.

Hex appears here, deliberately

Global is one of two homes for raw hex (the other is Modes brand DNA). Editing Global/color/system/error/500 updates every error surface across all 9 brands and both themes — that single-edit-point property is the reason Global exists.

06Collection 2 · Modes

Brand DNA + brand-tinted chrome.

140 variables across 9 brand modes. Brand DNA holds the only raw hex outside Global. Everything else in Modes is an alias to brand DNA or Global.

Brand DNA · the raw-hex layer

  • color/primary/{50..900} — 10 stops
  • color/secondary/{50..900} — 10 stops
  • color/neutral/{50..900} — 10 stops, brand-tinted
  • color/tertiary/{50..900} — 10 stops, accent

40 raw hex tokens × 9 brand modes = 360 raw hex values. The only place outside Global where hex is allowed.

Why tertiary was added

Lyllo's accent is orange (#FD6A00) but its primary is pink and its secondary is also pink-leaning. There was no clean home for a third accent without forcing it into a non-DNA token. Tertiary unblocks brands that need a third hue — Comeon lime (#C3FF00), future palettes — and stays at zero cost for brands that don't (other 7 brands mirror primary as a placeholder).

Alias chrome inside Modes

Every other Modes token aliases DNA — radii, brand chrome (header, banner, landing, menu-row, nav, surface, text, icon, border), typography family/weight, flag/meta strings.

Modes/color/header/surface-primary
  → Modes/color/neutral/600   # Lyllo: #5b5c60 (brand-tinted grey)

Modes/radius/cta
  → Global/scale/4           # 4px — rounded from legacy 3

Modes/radius/pill
  → Global/scale/160         # 160px — true pill
07Collection 3 · Theme

Light and Dark, 100% alias.

31 variables, 2 modes (Light, Dark). Every token in Theme is an alias — never raw hex. The Light alias chains through Modes to brand DNA; the Dark alias does the same with the dark-side stop.

theme/surface/page
  Light → Modes/color/surface/page
            → Modes/color/neutral/100   # brand-tinted off-white
  Dark  → Modes/color/surface/page
            → Modes/color/neutral/900   # brand-tinted near-black

theme/text/primary
  Light → Modes/color/text/primary
            → Modes/color/neutral/900   # near-black text on light
  Dark  → Modes/color/text/primary
            → Modes/color/neutral/100   # off-white text on dark

theme/nav/shadow
  Light → Global/color/black-alpha/10
  Dark  → Global/color/black-alpha/0    # no shadow needed on dark

Why theme tokens never hold hex

If theme/surface/page ever held a raw hex, that hex would be wrong for at least 8 of 9 brands. The alias forces the resolution to flow back into the brand-tinted neutrals, so Lyllo's "page" is its own off-white/near-black, not a generic grey.

Theme is the cheap toggle

A single frame mode flip — Theme = Dark — switches every Theme alias's resolution to its Dark target. Brand modes stay where they are. Component bindings don't change. Add a per-brand dark stop in Modes once, every Theme token picks it up.

08Collection 4 · Semantic

Role tokens for designers and devs.

53 variables, single mode, 100% alias. Spacing, icon-size, and 36 colour roles. Components bind Semantic when they need a role-named token without component-specific naming.

Semantic spacing scale
TokenAliases
spacing/xxsGlobal/scale/4
spacing/xsGlobal/scale/8
spacing/smGlobal/scale/12
spacing/mdGlobal/scale/16
spacing/lgGlobal/scale/24
spacing/xlGlobal/scale/32
spacing/2xlGlobal/scale/48
spacing/3xlGlobal/scale/64
Semantic icon size
TokenAliases
icon-size/smGlobal/scale/16
icon-size/mdGlobal/scale/20
icon-size/lgGlobal/scale/24
icon-size/xlGlobal/scale/32

Plus 36 colour aliases mapping common roles (text, surface, border, icon, status, brand) to Modes or Theme tokens.

09Collection 5 · Component Packs

Last-mile alias packs for component masters.

38 variables. Component-specific aliases that sit one layer above Semantic — used when a component master needs a token name that does not generalise (e.g. menu-row/label/on-icon).

Component Packs/menu-row/icon
  → Semantic/color/icon/content
    → Theme/color/icon/content
      → Modes/color/icon/content
        → Modes/color/neutral/700

Component Packs/cta/primary/background
  → Semantic/color/brand/primary
    → Modes/color/brand/primary
      → Modes/color/primary/500

Component Packs/header/active-selected
  → Modes/color/header/active-selected
    → Modes/color/primary/500   # NOT neutral — must contrast surface-primary

Header rule

header/active-selected must never alias the same Modes/neutral stop as header/surface-primary for a brand — that produces an invisible nav active state on coloured headers. Pin active-selected to a brand stop (primary/500) instead. This rule is locked in the component audit.

10Invariant 1

Raw hex lives in two homes. Everywhere else is an alias.

This is the discipline rule. It is enforced by a single Plugin API audit that any session can run.

Raw hex allowed in

  • Global/color/black, /white
  • Global/color/black-alpha/{0..90}
  • Global/color/white-alpha/{0..90}
  • Global/color/system/{success,warning,error,info}/{50..900}
  • Modes/color/primary/{50..900}
  • Modes/color/secondary/{50..900}
  • Modes/color/neutral/{50..900}
  • Modes/color/tertiary/{50..900}

Raw hex forbidden in

  • Modes (outside primary/secondary/neutral/tertiary) — brand/*, surface/*, text/*, icon/*, border/*, banner/*, landing/*, menu-row/*, nav/*, header/*, chrome/*
  • Theme — every var in both modes
  • Semantic — every var
  • Component Packs — every var
  • Regulatory — every var

Audit: getLocalVariablesAsync() filtered to COLOR; assert non-DNA / non-Global vars resolve to VARIABLE_ALIAS in every mode.

11Invariant 2

Strict 4-point scale. No exceptions.

Global/scale/* contains only 0 or multiples of 4. Twenty stops. Off-grid legacy values round to the closest stop — type rounds up, radii round to closest.

Canonical scale (20 stops):
0, 4, 8, 12, 16, 20, 24, 28, 32, 40,
48, 56, 64, 72, 80, 96, 112, 128, 144, 160
Legacy values rounded to canonical stops
LegacyRounded toWhy
3 (radii)scale/4Closest stop
6 (radii)scale/4 or scale/8Closest stop · team to choose
100 (radii)scale/96Closest stop
11 (type)scale/12Round up
15 (type)scale/16Round up
17 (type)scale/20Round up
19 (type)scale/20Round up
22 (type)scale/24Round up
36 (line-height)scale/40Round up
50 / 60 (display)scale/48 / 64Closest stop

The exception is letter-spacing

typography/letter-spacing/{tight, normal, wide} are raw FLOAT (fractional values like ±0.02). They live in their own micro-scale and don't pretend to fit scale/*.

Why the rule is strict

Once you allow off-grid stops "just for this radius", every legacy value becomes a candidate. The scale erodes into a list of historical accidents. A 1–4px visual drift on a few radii is a one-time cost; carrying off-grid stops forever is a recurring cost on every designer.

12Nine brand modes

Same DNA, nine personalities.

Each brand mode in Modes overrides the four DNA ramps. Everything else in Modes (chrome aliases, radii, typography family, meta strings) re-resolves automatically when the frame's brand mode flips.

Brand DNA primary anchors
Brand primary/500 secondary/500 tertiary/500 Notes
Lyllo#F02884 pinkpink-leaning#FD6A00 orangeVisible · Tertiary unblocks orange accent
SnabarregreenblueplaceholderVisible · Header lives in blue
Mobilespinredred-deepplaceholderVisible · Icons stay charcoal
Comeongreengreen-deep#C3FF00 limeModes-only · Tertiary lime accent
Mobilebetgreengreen-deepplaceholderModes-only · No frame yet
Get Luckyorangepink-magentaplaceholderModes-only · Dual-palette
ReviantredwarmplaceholderModes-only
888 CasinoyellowblackplaceholderModes-only · Yellow CTAs
Reviant 2red-pinkblue-purpleplaceholderModes-only · Sharp-edge variant

Three brands have visible frames in fysBbW10LRJUNgisaBKXJo; the remaining six exist as Modes entries ready for future visual coverage.

13Migration map

v3.1 → v3.2 in one table.

Renames preserve binding IDs in Figma — components pick up new names without per-instance edits.

v3.1 to v3.2 token migration
v3.1 token / familyv3.2 locationChange
color/brand/primary-NModes/color/primary/NRenamed · group flat
color/brand/secondary-NModes/color/secondary/NRenamed
color/brand/tertiary-NModes/color/tertiary/NPromoted from optional
color/neutral-NModes/color/neutral/NBrand-tinted, moved to Modes
color/status/*Global/color/system/*Renamed · Global, brand-invariant
color/always/{black,white}Global/color/{black,white}Flat in Global
spacing/NGlobal/scale/N + Semantic/spacing/{xxs..3xl}Two-layer · scale + named
radius/{base-3,input,box,card,pill,navigation,large}Modes/radius/* → Global/scale/*All aliased to scale
icon/sizesSemantic/icon-size/{sm,md,lg,xl}Promoted to Semantic
elevation tokensGlobal/elevation/0..4 (Effect Style)Effect Styles · 5 levels
launcher/* (deferred)Stays deferred
platform/* (deferred)Stays deferred
14Permission matrix

Who can edit what.

Marketing edits brand DNA only. Compliance edits Regulatory. The DS lead owns the rest. This boundary is what makes the architecture survive contact with day-to-day work.

Permission matrix per collection
CollectionMarketingComplianceDS leadEngineering
Global✓ writesread-only via export
Modes · brand DNA✓ writes (only here)reviewread-only
Modes · alias chrome✓ writesread-only
Theme✓ writesread-only
Semantic✓ writesread-only
Component Packs✓ writesread-only
Regulatory✓ writesreviewread-only

The single-edit-point property of brand DNA is what enables marketing self-service: edit one ramp stop, the whole chain updates. Component bindings never need touching at the marketing level.

15Component adoption

v3.2 ships in the variable layer. Components are next.

The variables landed on 2026-04-29. Component masters on the Components page still bind earlier library names — see the component audit for the full gap analysis and remediation plan.

Done

  • 6 collections rebuilt in fysBbW10LRJUNgisaBKXJo
  • 9 brand modes seeded with DNA
  • Tertiary ramp added across 9 brands
  • Strict 4-point scale enforced (off-grid stops removed)
  • Theme alias-only audit passes
  • Hex invariant audit passes
  • Live HTML reference (this set)

Outstanding

  • Component-master rewire — components still bind Main/*, Generic/*, Lobby Header/*, Whiteframe/*
  • Focus tokens — none defined yet (WCAG 2.4.7 / 2.4.11 / 2.4.13)
  • Notification text-on-color contrast — confirmed AA failures (success, warning, info)
  • Touch target sizes < 24×24 in Toggle thumb, Notification ×, Modal ×, Micro Button
  • "OLD" components retiring (Payment Method OLD, duplicate Sub Tabs)
  • Per-brand dark-mode neutrals beyond the placeholder set
v3.2 · Final

Six collections.
Two invariants.

Edit brand DNA. Everything else flows. Audit at any time with one Plugin API call. The architecture is locked — the work that remains is component adoption.

Token Reference v3.2 Component Audit Home