Work

CampaignHQ · Design system

One design system for CampaignHQ and Kumo, the company behind it: two brands and two themes from one library

CampaignHQ is a WhatsApp, email and SMS marketing platform. Kumo is the company that builds it (the KumoHQ of my Kumo Board case study). Neither had a design system, so over four months I built one from scratch.

One screen, two switches, in 10 seconds. The theme switch turns it dark. The brand switch turns it into Kumo: its colours, its typeface and its button sizes. Nothing in the screen is redrawn.
My role
Sole product designer
Team
Founders, PM, developers
Timeline
4 months
Status
In use
What
A design system for CampaignHQ and for Kumo: colours, type, sizes and 20 components, in Light and Dark.
Why
Design, development, sales, marketing and growth each drew their own CampaignHQ, and nothing tied them together.
The idea
Screens ask for a role, like "main text", never a colour. One switch picks the theme, another picks the brand.
The hard call
20 components, not 60. Only what a real screen needed, so one designer could keep it right.

The problem

Every team was drawing its own CampaignHQ

There were no shared colours, type or components. Just the website and two brands' colours, copied by each team its own way.

  1. Every screen started from nothing

    Buttons, tags and text were drawn by hand each time, so two screens rarely matched.

  2. Each team had its own shades

    Product, sales, marketing and growth picked colours by eye, so the same brand looked different on a screen, a deck and a post.

  3. Dark mode meant redrawing everything

    With colours typed into every screen, a dark version was a second copy of the product.

  4. A second brand meant a second product

    Kumo needed the same screens in its own colours and type, which would double every change.

What it looked like

Screens and slides that didn't quite match.

The problem underneath

Nothing was decided once. Every colour, size and typeface was decided again on every screen, by every team.

Uber
Keep raw colours apart from what they mean, so a theme can change one without touching the other.
IBM
Name tokens by their job, like "text primary", so the name says where it goes.
Atlassian
Put the usage note right on the component, where people already look.

The systems I studied first, and the one idea I took from each.

01 · Three levels

A screen asks for a role, never a colour

A token (in Figma, a variable) is a named value, like Text/Primary, that holds a colour or a size. I split them into three levels, each pointing to the one below.

Define every colour once

Brand holds every raw colour. Alias gives each one a role, like Primary or Error. Mapped says where a role is used, like Text or Surface. Screens and components only ever use Mapped.

The Brand, Alias, Mapped sheet in Figma, with one token followed through all three levels
The foundations sheet in Figma. Follow Text/Error across: it points to Alias Error, which points to a red in Brand. Light and Dark land on different reds.

Put each switch on one level

A mode is a set of values a token can switch between. Themes (Light, Dark) are modes of Mapped; brands (CampaignHQ, Kumo) are modes of Alias. A theme changes where a role is used, a brand changes what it means, so the two switches never collide.

The Mapped tokens sheet: each token with a Light value, a Dark value and a note on where it's used
The Mapped sheet. Every token a screen can use, with its Light value, its Dark value and where it goes.
02 · Dark mode

Dark mode is designed, not inverted

Drag to compare · the same frame, theme switched
The list screen in Dark
The list screen in Light
LightDark
Same screen, both themes. In Dark, panels get lighter as they come forward, so depth reads without shadows, and the main button flips from navy to cream.

Switch the whole screen from one place

Select a frame, open the theme in Figma's Appearance panel, pick Dark. Every component inside follows, because none of them holds a colour of its own.

A selected screen in Figma with the Appearance panel's theme dropdown open and Dark ticked
The switch in Figma. One dropdown on the frame. Nothing inside it is touched.
03 · A second brand

Kumo is one more mode, not a second library

Both brands already had their colours; each team just used its own version. I didn't pick new ones. I tied the existing ones into one system.

Prove it on a real Kumo screen

Recolouring a CampaignHQ screen only proves colours swap. So I rebuilt My work, the home screen of Kumo's own project tool, from the same components.

Kumo · LightKumo's My work screen in Light, built from the design system's components
Kumo · DarkKumo's My work screen in Dark
My work, built from the system. Look at the coral button, the 36px sidebar rows and the Geist headings: all of it comes from the brand switch.

Let a brand carry type and sizes too

Kumo isn't CampaignHQ in new colours. Its headings use Geist, its buttons are 42px and its sidebar rows 36px. So Alias holds a type scale and control sizes per brand, not just colours.

The Alias sheet: each role with its CampaignHQ ramp and its Kumo ramp side by side
The Alias sheet. One row per role, one column per brand. The note at the bottom lists what else a brand carries: its main action, typefaces, type scale and sizes.

Give the main action its own role

CampaignHQ's buttons and text share one navy. Kumo's text is navy but its buttons are coral. So buttons got their own role, Action, and each brand fills it in.

Keep what status means, let the shade vary

Red is an error and green is done in both brands, but each brand uses its own shade.

Ruled out

Badges with Kumo's logo red as the accent: New and Failed look the same

Kumo's logo red as the product accentIt sat 0.4° of hue from the error red, so a "New" badge read as a failure. The red stays in the logo; inside the product, Kumo's accent is coral.

Kumo's dark mode on indigo surfaces

Kumo's indigo for dark surfacesIt read as heavy purple. Neutral graphite lets Kumo's coral lead instead.

Moving warning to goldCampaignHQ's orange sat a few degrees from the warning amber. Gold gave olive text, so I tuned the orange's tints instead.

04 · The system

Only what a real screen needed

Set type in the brand, not the style

Newsreader for titles, Inter for everything else. Each style's typeface and size come from Alias, so Kumo's headings switch to Geist without a second set of styles.

The typography sheet: each text style with its spec, a live sample and where it's used
The text styles, in CampaignHQ. Each row: the style, a live sample and where it's used.

One component instead of every drawing

A component is a reusable piece, like a button; its variants are its versions, like Primary or Disabled. Tags drawn three different ways became one Badge: the tone carries the channel or status, the size carries the density.

The Badge component: eight tones in two sizes
The Badge. Eight tones, two sizes. Every variant uses Mapped tokens, so it follows both switches.

Never let colour carry meaning alone

Each callout tone has its own icon, so a warning still reads as a warning in either brand and either theme, and for anyone who can't tell amber from coral.

The Callout component: five tones, each with its own icon
The Callout. Info, tick, triangle, octagon and lamp: one icon per tone.
Button, Checkbox, Radio and Callout components on the Figma canvas
Components on the Figma canvas. The header checkbox has a "some selected" state because a screen review asked what it should show when 2 of 5 rows are picked.

The hard call

20 components, not 60

Decision
Ship 20 components, each one proven by a real screen, and leave the rest out.
Why
Every component has to stay right in two themes and two brands. A smaller library that's always right beats a bigger one nobody trusts.
What it changed
New components arrive with the screen that needs them, so the library grows with the product.

A component joins with its screen

No speculative parts. Each one comes with the screen that proves it.

Every component explains itself

Usage notes sit in each component's description, so hovering it in Figma says when to use it.

Tokens speak code

Every token carries its CSS name, so a developer reads var(--surface-action), not a hex code.

No loose colours

An audit finds any colour not tied to a token. In the components today, it finds none.

In use

Four teams build from one library

A second brand
Kumo runs from one Alias mode. The components were set up once to read each brand's sizes, and no screen was redrawn.
Development
In Dev Mode, Figma's handoff view, a primary button reads var(--surface-action), the same name the code uses.
Sales and marketing
Decks and posts use the same logo, type and colours as the product.
CampaignHQ's list screen in Light and Dark above Kumo's My work screen in Light and Dark
Two products, two themes, one library. Top: CampaignHQ in Light and Dark. Bottom: Kumo's My work screen in Light and Dark.

What I learned

Three things I'll carry into the next system

  1. Measure before you argue

    A 0.4° hue gap settled Kumo's accent faster than any opinion about colour.

  2. A brand is more than its colours

    Kumo needed its own typeface and sizes. Next time the brand level holds numbers from day one.

  3. Colour always needs a backup

    Every clash I fixed with colour, I also fixed with shape or words: icons on callouts, labels on badges.

Next for the system: the Stepper's final direction, chosen with the founders, and templates for sales and marketing.

You made it to the end. Thanks for reading :)