There are design decisions that come back to bite you later. Color is one of them. At the beginning, it always feels like a “later” problem, “Let’s just get it working first.” Fast-forward a few months, and you’re the poor soul refactoring #0FB9B1 from 17 different files because marketing decided the primary turquoise needs to be 5% darker. Lovely.

Theming isn’t a luxury. It’s hygiene.

Theming, the separation of visual styling from component logic, usually gets introduced after requirements scream loud enough. Light/Dark mode. Whitelabel support. Multi-tenancy. Accessibility. Take your pick.

But why wait?

If you know these things might show up down the line, there’s zero reason not to prep for them from day one. It’s not overengineering. It’s laying down the tracks before the train speeds up.

The Practical Starting Point: Color Token Design

Solid theming starts with a system. Not just defining --primary and calling it a day. You need structure. Context. Semantics.

This is how you build it.

Color Roles

Think in roles, not colors. Not “blue,” but “Info.” Not “green,” but “Success.” Your UI communicates states, your tokens should reflect that.

RolePurpose
BasicNeutral foundation (e.g., form fields)
PrimaryMain interactive elements
InfoContextual or neutral information
SuccessPositive outcomes
ErrorValidation errors or destructive actions

State Variants

Colors have states. A button doesn’t look the same when it’s hovered, focused, disabled, or selected, and your system should reflect that.

StatePurpose
Default Standard visual
Muted Disabled or low priority
Highlight Hover/Focus state
Strong Active/Selected state
Facing Text or content on top of the color

Elevation Levels

Don’t forget backgrounds. Especially when dealing with nested layouts or UI depth, a layered token model is a must.

1--level-0: #FFFFFF; /* Default background */
2--level-1: #F5F8F9; /* First level nesting */
1--level-0: #FFFFFF; /* Default background */
2--level-1: #F5F8F9; /* First level nesting */

Selling B2B? Theming Is Non-Negotiable

If your software is meant for multiple clients, especially in the B2B or SaaS world, then theming isn't just a nice-to-have. It's a hard requirement.

Every customer will want their own logo. Sure. But it won’t stop there. The moment one of them asks for “our brand colors,” you either:

  1. Pray they’re close to your default palette, or

  2. Spend an unholy amount of time building what you should’ve started with: a theme system.

Supporting custom branding at scale means color flexibility is baked into your foundation. When each client gets their own palette, your design system must decouple style from structure.

Tokens are your scalable way forward.

Okay... But What If I Haven’t Done This Yet?

If you’re halfway through a project (or deep into one) and realizing you skipped theming, don’t panic. The good news? You probably don’t need a massive refactor to get back on track. You just need to start now, and you can do it incrementally.

Create a Theme CSS File

Start by defining your design tokens in a dedicated theme scope — usually tied to a root class like .my-theme or :root.

1.my-theme {
2  /* LEVEL */
3  --level-0: #ffffff;
4  --level-1: #f5f7f9;
5  
6  /* BASIC */
7  --basic-default: #ffffff;
8  --basic-muted: #e9ecef;
9  --basic-highlight: #dfe3e7;
10  --basic-strong: #b0b8c1;
11  --basic-facing: #1c1e21;
12
13  /* PRIMARY */
14  --primary-default: #2f5aa8;
15  --primary-muted: #d5deef;
16  --primary-highlight: #4a73c2;
17  --primary-strong: #24488c;
18  --primary-facing: #ffffff;
19
20  ...
21}
22
1.my-theme {
2  /* LEVEL */
3  --level-0: #ffffff;
4  --level-1: #f5f7f9;
5  
6  /* BASIC */
7  --basic-default: #ffffff;
8  --basic-muted: #e9ecef;
9  --basic-highlight: #dfe3e7;
10  --basic-strong: #b0b8c1;
11  --basic-facing: #1c1e21;
12
13  /* PRIMARY */
14  --primary-default: #2f5aa8;
15  --primary-muted: #d5deef;
16  --primary-highlight: #4a73c2;
17  --primary-strong: #24488c;
18  --primary-facing: #ffffff;
19
20  ...
21}
22

This becomes your palette, one place to rule all styles.

And because the structure is consistent, you can now define a new theme and swap with a single class change:

1.my-dark-theme {
2  /* LEVELS */
3  --level-0: #1a1a1a;
4  --level-1: #252525;
5
6  /* BASIC */
7  --basic-default: #2f2f2f;
8  --basic-muted: #3a3a3a;
9  --basic-highlight: #4c4c4c;
10  --basic-strong: #666666;
11  --basic-facing: #d0d0d0;
12
13  /* PRIMARY */
14  --primary-default: #f72585;
15  --primary-muted: #4b1e38;
16  --primary-highlight: #fa58aa;
17  --primary-strong: #ff7cb8;
18  --primary-facing: #e8cce0;
19...
20}
1.my-dark-theme {
2  /* LEVELS */
3  --level-0: #1a1a1a;
4  --level-1: #252525;
5
6  /* BASIC */
7  --basic-default: #2f2f2f;
8  --basic-muted: #3a3a3a;
9  --basic-highlight: #4c4c4c;
10  --basic-strong: #666666;
11  --basic-facing: #d0d0d0;
12
13  /* PRIMARY */
14  --primary-default: #f72585;
15  --primary-muted: #4b1e38;
16  --primary-highlight: #fa58aa;
17  --primary-strong: #ff7cb8;
18  --primary-facing: #e8cce0;
19...
20}

Refactor Styles to Use Tokens

Apply the tokens in your core components. Here’s a simple button example:

1.button-base {
2  background-color: var(--basic-default);
3  color: var(--basic-facing);
4
5  &.disabled {
6    background-color: var(--basic-muted);
7  }
8
9  &:not(.disabled) {
10    &:hover {
11      background-color: var(--basic-highlight);
12    }
13    &:active {
14      background-color: var(--basic-strong);
15    }
16  }
17}
18
19.button-primary {
20  background-color: var(--primary-default);
21  color: var(--primary-facing);
22
23  &.disabled {
24    background-color: var(--primary-muted);
25  }
26
27  &:not(.disabled) {
28    &:hover {
29      background-color: var(--primary-highlight);
30    }
31    &:active {
32      background-color: var(--primary-strong);
33    }
34  }
35}
1.button-base {
2  background-color: var(--basic-default);
3  color: var(--basic-facing);
4
5  &.disabled {
6    background-color: var(--basic-muted);
7  }
8
9  &:not(.disabled) {
10    &:hover {
11      background-color: var(--basic-highlight);
12    }
13    &:active {
14      background-color: var(--basic-strong);
15    }
16  }
17}
18
19.button-primary {
20  background-color: var(--primary-default);
21  color: var(--primary-facing);
22
23  &.disabled {
24    background-color: var(--primary-muted);
25  }
26
27  &:not(.disabled) {
28    &:hover {
29      background-color: var(--primary-highlight);
30    }
31    &:active {
32      background-color: var(--primary-strong);
33    }
34  }
35}

Start small, pick high-traffic components like buttons and forms. Over time, your codebase will shift from brittle and hardcoded to flexible and theme-aware.

TL;DR: Theming Isn’t a Later Decision

If your product is going to grow, team-wise, feature-wise, or audience-wise, theming from day one is the smarter path. Not for the hype, but for the foundation.

Start with color tokens. Think in roles and states. Stop seeing your UI as “blue on white,” and start seeing it as “Primary on Level-0.”

Your future self will thank you.
Your future clients will demand it.
And let’s be honest, you wanted a Dark Mode anyway.