

About
Project
Design tokens create a shared language between design and development—bridging aesthetics and implementation through consistent, scalable design values. In V1, I built a robust system for a Fortune 500 product to tackle deep UX debt. In V2, I simplified that system to make it more usable without sacrificing structure or flexibility. Through this process from V1 to V2 I added +20 to the NPS score amongst designers using the system.
Overview
Goal: Reducing inconsistency and increasing scaled usability for designers and developers
Role: Design System Architect
Duration: V1 - 2 months, V2 - 1 month
Problem
I inherited design files burdened with years of UX debt—over 30 button styles and countless inconsistencies in color and component usage. While components and color styles helped to some extent, they often broke when designers needed a more custom component. Designers would override components or apply colors not used in the style guide, leading to subtle but widespread visual drift. What we needed was a more robust, systematic approach—one that would eliminate guesswork, enforce consistency, and scale effortlessly across platforms while preventing old habits from resurfacing.
Solution
I Introduced design tokens as a shared language between design and development—a unified source of truth for all visual styles.
Token Definition
A Design Token is a source of truth for designers and developers. It represents design values, and can be used to take granular control of UI at scale. Known within Figma as variants.
Global Token
Alias Token
Component Token
Initial Results
V1 Results
Good
Reduced inconsistency, solving UX debt.
Reduced guesswork and enforced consistency.
Scaled to other platforms and projects.
Created a cohesive language between design and development.
Bad
Steep learning curve for designers to adopt.
High cognitive load due to naming complexity.
Inefficiencies in system that needed to be reduced.
Net Promotor Score (NPS) of V1
Designers: +70
Developers: +100
V1 to V2
1st Key Difference - Color Scale
V1 was built with the color scale as the foundation—surfaces, borders, and text were then fit into that system based on predefined ranges. In contrast, V2 started with the unique needs of each element. Surfaces, borders, and text were treated as distinct entities, each requiring different levels of contrast and expression. This shift led to a more dynamic and purpose-driven color scale, rather than a rigid, incremental one.
V1
Predefined surfaces, borders, and content scales. Represented in light mode and without inverse tokens for simplicity.
V2
Dynamic surfaces, borders, and content ranges.
2nd Key Difference - Surfaces & Text
In V1, content and surfaces followed a strict one-to-one relationship: as a surface moved up the color scale (toward a lighter value), the content on top adjusted incrementally to maintain the same contrast level. While this guaranteed consistency and accessibility, it also introduced unnecessary rigidity and complexity. Complexity because content hierarchy still needed to be considered. For each surface and content relationship, up to 3 hierarchical levels could be present with weak, medium, and strong content tokens.
In V2, I broke that dependency. Surfaces were designed with more subtle differences in light stops, allowing for smoother layering and more options for content. Content hierarchy was decoupled from the surface it sat on, and instead expressed through opacity—with stronger text appearing at full opacity and lower-priority text using reduced opacity. This shift reduced the need for multiple sets of content tokens for each surface level.
The result: a cleaner, more flexible system that still met accessibility standards—even against the darkest surfaces—while significantly reducing cognitive load for designers. Instead of navigating a rulebook, they could focus on design principles.
V1
Surfaces and content linked together for consistent contrast. Plus creating hierarchy.
V2
Text can be mixed and matched with surfaces.
3rd Key Difference - State Layers
In V1, changing a component’s state required swapping the entire token—designers had to decode the current token to determine its state-based equivalent. For example, updating content01.medium.primary to a pressed state meant manually changing it to content01.medium.primary-pressed. This approach was functional but not intuitive.
In V2, I introduced state tokens that could be layered on top of the base token. Rather than replacing the entire style, designers simply applied a universal modifier like state-pressed, state-hover, or state-disabled to the fill. This dramatically improved clarity and speed—designers no longer needed to decode token names, just apply the appropriate state.
Each state token was carefully crafted using curated opacity and color adjustments to align with the intended sentiment—whether it was a subtle hover, a clear pressed state, or a muted disabled style. The result was a system that was easier to use, easier to maintain, and still visually accurate.
V1
States move up or down the scale.
V2
States layers added to the default fill.
4th Key Difference - Edge Cases
In V1, I introduced “inverse” tokens to handle surfaces requiring stronger contrast—typically darker surfaces in light mode or lighter ones in dark mode. This approach worked well for standout elements like call-to-action buttons but came with added complexity.
With V2, I streamlined the system. The need for inverse-like surfaces still existed, but I created a clearer distinction between regular surface colors and semantic colors. Only semantic values—those tied to meaning, like alerts or primary actions—require both light and dark variants for each theme. All other surfaces now align directly with the active theme, either light or dark.
V1
Inverse options allow dark surfaces and light content while in light mode.
V2
Semantic values provide weak and strong options to replace the need for inverse complexity.
This refinement also made theme switching feel more natural and dynamic. For cases where a designer wants a neutral surface to behave like an inverse (e.g., a dark side panel in light mode), the system allows that panel layer to render in dark mode independently of the global theme. When switching themes, the panel remains dark—preserving visual continuity and avoiding jarring flips.
By simplifying semantic rules and giving neutral surfaces more nuanced behavior, I reduced visual noise, avoided over-tokenization, and provided designers with fewer, but more intentional, options. The result: a more thoughtful, flexible system grounded in real-world design needs.
Side panel remains in dark mode.
5th Key Difference - Powerful Theming
In V1, light and dark mode logic was handled at the global token level. Since choosing dark mode colors isn’t as simple as reversing the scale, I opted to swap the entire palette with a curated set of dark mode values. While effective in some ways, this approach proved limiting.
In V2, I shifted to a single unified palette. Instead of swapping the full global set, light and dark mode behavior was now managed at the alias level—where each alias could reference different global tokens depending on the mode. This freed up the global palette, allowing full color swaps to be reserved for entirely different themes rather than just light/dark modes.
The result was a far more flexible system—one that enabled true cross-pollination of token usage. With this separation, I could now change both theme (e.g., brand palettes) and mode (light or dark) independently or in tandem. Designers and developers could switch themes on the fly, and the system would intelligently remap visuals without breaking structure or intent.
V1
Light Mode

Dark Mode

Results from swapping global values.
V2
Light Mode
Theme 1

Light Mode
Theme 2

Dark Mode
Theme 1

Dark Mode
Theme 2

Results from swapping alias and global values.
But what about developers
Although the new system was designed to be simpler and more approachable for designers, I was careful not to disrupt what was already working well for developers—or introduce a system that couldn’t be implemented technically. My biggest concern was supporting multiple tokens for a single fill, a concept central to the new alias and state layering model. I collaborated closely with our staff engineer to validate this approach. He confirmed it was not only feasible, but that the structural changes on his end were minimal. The update was considered trivial to implement—but delivered a high impact across design and development workflows.
V2 Results
Good
Kept the reduction of UX debt from V1 .
Maintained cross-platform scalability while preserving a unified design and development language.
Smaller learning curve for designers to adopt.
Token naming conventions were simplified.
System inefficiencies were reduced.
Bad
More complexity behind the curtain making it more difficult to architecturally update by other designers if needed.
Net Promotor Score (NPS) of V2
Designers: +90 (+20 from V1)
Developers: +100 (no change)
Designers shifted from passive supporters in V1 to active promoters in V2. Developers maintained their strong support and continued to find the system reliable and intuitive.
What we did next…
We applied tokens to typography, spacing, borders, and elevation for a holistic and robust design system.