Rebuilding our design system with semantic tokens

Rebuilt our colour foundation with tokens. Normalised ramps, mapped every component to semantics, and shipped light/dark with brand parity. Faster theming, cleaner code, fewer surprises.

Client

NewU

NewU

NewU

Year

2025

2025

2025

Project overview


Scope: Build a themeable colour system, map to semantic roles, add component aliases, add dark-mode parity, remap all components, and document usage.

TL;DR - Colour primitives → semantic roles → component aliases → dark mode

Goal: Cut design time by ~50%, speed development similarly, map 100% of components to tokens, and meet WCAG AA across core screens.

My role: Project Lead (System architecture, naming, variables, dark aliases, component remap)

Team: Emmanuel Job, Nkiru Uba

Result: 100% components mapped to semantic tokens; normalised ramps (5→90); light/dark parity


Inconsistent UI slowed teams and confused users


Our design files mixed raw primitive hex values into components, colour ramps were uneven (20, 40, 80…), and there was no reliable way to theme light/dark or switch brands. Every change meant hunting through layers and hoping nothing broke.


  • Components referenced primitives directly

  • Inconsistent ramps and brand steps

  • Unnecessary primitive colours

  • Dark mode was a “repaint,” not a theme

  • Custom theming took a long time to complete per project, which was not scalable.



Old colour ramps 1.1


We set a clear goal - one themeable system, light and dark


  1. Normalise ramps to a consistent 5→90 scale for greys and brands

  2. Introduce semantic roles so components never point to primitives

  3. Ship dark aliases that mirror light names one-to-one

  4. Document the system so future work is fast and predictable

  5. Create business opportunities by adding custom theming for clients



I audited colours and normalized ramps before naming roles


Primitives → Roles → Components

  • Primitives: pure ramps (e.g., Colours.Gray neutral.5, 10… 90, Primary, Secondary).

  • Roles (semantic): intent names the team understands (e.g., colour.bg.card, colour.text.primary, colour.action.primary.bg).

  • Component aliases (optional): friendly pointers for slots (e.g., colour.comp.button.primary.bg → colour.action.primary.bg).

If brand changes or dark mode flips, we update roles, not components.


Simple token architecture diagram 2.0


Components now refecrence aliases instead of raw values


  • Greys: Switched to Gray neutral ramp (5→90) for canvas, elevation, borders, disabled.

  • Brands: Two families, Primary (purple) and Secondary (orange), each with hover/active steps.

  • Foreground on strong fills: colour.fg.on.brand.* Tokens guarantee readable text/icons on brand backgrounds.

  • Actions: three families — Primary (purple), Accent (orange), Neutral (grey) — plus Ghost and Link.


New Semantic variables snapshot 3.0


Light & Dark


We mirrored names across modes. No new nouns, just different aliases.


Light vs dark theme 4.0


Implementation


A focused, repeatable cadence over 4 weeks.

  • Week 1: Audit colour usage, list primitives in use, define naming rules

  • Week 2: Build variables (primitives), create roles, map all core components

  • Week 3: Dark aliases, action families, migrate remaining components

  • Week 4 +: QA pass (contrast, leaks), docs, library clean-up and attaching aliases to recent project files.


Every component that was remapped and documented


  • Role tables with “what it’s for” in plain English (bg.page, bg.card, text.primary, border.default, action sets).

  • Component alias map for buttons, inputs, cards, and overlays.


Semantic token documentation 5.0


Before → After


  • Before: hard-coded hex, uneven ramps, manual dark/custom theme edits

  • After: token-first system, normalised ramps, light/dark parity, faster theming


Before vs after 6.0

Create a free website with Framer, the website builder loved by startups, designers and agencies.