Token Migration at Dusk

Semantic tokens and multi-brand theming from a legacy codebase

Company
Dusk Technologies
Role
Senior Design Systems Designer
Year
2024
Duration
7 months
TokensMigrationTheming

title: "Token Migration at Dusk" tagline: "Semantic tokens and multi-brand theming from a legacy codebase" company: "Dusk Technologies" role: "Senior Design Systems Designer" year: "2024" duration: "7 months" tags: ["Tokens", "Migration", "Theming"] coverAccent: "var(--color-accent-amber)" status: "published"

Overview

Dusk had been acquired by a larger enterprise platform and needed to white-label their product for three enterprise clients — each with distinct brand requirements, strict accessibility standards, and zero tolerance for visual inconsistency.

The challenge: 1,247 hardcoded hex values in the codebase. No token file. No Figma variables. Seven years of accumulated styling decisions with no shared language.

The Problem

Every brand request was effectively a fork. The engineering team estimated a full white-label for a new client would take six weeks. Client contracts required delivery in two. The business was blocked.

The surface-level problem was the codebase. The deeper problem was that there had never been a separation between "this is a raw value" and "this is what this value means." Color wasn't semantic — it was just color.

Audit Findings

The first thing I did was run a regex scan on the codebase.

SwatchTokenValueUsage
Before: #1a73e8hardcodedUsed in 47 places, 6 different semantic roles
Before: #202124hardcodedUsed in 89 places, 4 different semantic roles
--color-action-primaryvar(--color-blue-600)All interactive/CTA elements
--color-text-primaryvar(--color-gray-900)All primary text
--color-surface-basevar(--color-gray-0)Page and card backgrounds

The 89 unique values collapsed to 34 semantic tokens. Many hex values were near-duplicates that had drifted over time — #1a73e8 and #1b74e9 appearing in different parts of the same flow.

System Decisions

Token layer before component layer. The strategic decision that made everything else possible: we would not touch a single component until the token system was stable. Touching components first means touching them twice.

Primitive → Semantic → Brand. Three layers:

  1. Primitive tokens: raw values, brand-neutral (--color-blue-600: #2563eb)
  2. Semantic tokens: meaning-based (--color-action-primary: var(--color-blue-600))
  3. Brand overrides: [data-theme="brand-a"] semantic overrides only

No brand ever touches a primitive directly. Brand tokens only override semantic tokens.

Automated codemods for the migration. Manually replacing 1,247 instances is error-prone and expensive. We wrote jscodeshift transforms that matched hardcoded hex values and replaced them with the appropriate semantic token. The transforms were conservative — they flagged ambiguous cases (a hex used in multiple semantic roles) for manual review rather than making a guess.

Implementation

Phase 1: Build the primitive token set and the semantic layer for Brand A (the original Dusk brand). Ship this in production. Verify no visual regressions.

Phase 2: Run the codemods. Each engineer reviewed their domain's flagged cases. 94% of replacements were automated; 6% required manual decision.

Phase 3: Build Brand B and Brand C token files. These are 40–60 line CSS files of [data-theme="brand-x"] overrides. That's it. The entire multi-brand capability lives in those overrides.

1,247 → 34

Hardcoded values replaced with semantic tokens

After Phase 2 completion

The Figma side: Migrated local styles to Figma Variables in parallel. Three variable collections — Brand A, B, C — with identical token names. Designers switch between brands with a single variable mode toggle.

Handling Edge Cases

Brand C had no concept of "danger" states — their product domain was messaging, not financial transactions. The semantic token --color-feedback-danger had no meaningful equivalent in their brand language.

Impact

<200 lines

CSS required to deploy a new brand theme

Brand C shipped in 4 days, not 6 weeks

3 brands

Shipped simultaneously on the same component codebase

Zero component duplication across brands

Learnings

The migration tooling was as important as the token system design. A well-designed token architecture that requires manual migration will stall. Automating the tedious parts let the team focus on the decisions that actually required judgment.

The business outcome here was unusually concrete — a new enterprise client can now be onboarded in a week instead of six. That number has been cited in sales calls. It's a rare case where design systems work directly contributed to revenue.