Case 01: One token set, seven brands
I designed Bloom's three-tier token architecture so one component library can carry seven brands, and a new carrier becomes a theme rather than a fork.
- Scale
- 7 brand themes (Zinnia and 6 carriers) on one variable set
- Theme overrides
- Most carrier themes change fewer than 40 of 731 variables
- Most customized
- 106 of 731 variables, for the heaviest carrier theme
The challenge
Zinnia grew through acquisitions: SE2, life.io, Breathe Life and Convergent became one company in 2022, and Policygenius joined in 2023. Their products arrived with no white-labeled design system and no design tokens, so every new brand meant overriding styles by hand, and every override became drift that engineering had to maintain.
Evidence: the Zinnia theme, from its Figma variables
| Core | Base | Component |
|---|---|---|
color-900-gray | surface-darkerBase · Surface | button-primary-default-fillButton · Primary |
color-white | text-lightBase · Text | button-primary-default-textButton · Primary |
color-secondary | selected-fill-altStates · Selected | toggle-selected-fillToggle |
| Token | Value |
|---|---|
color-primary | #FF7500 |
color-primary-lightest | #FFEAD9 |
color-primary-dark | #994600 |
color-secondary | #00628B |
color-secondary-lightest | #D9E7EE |
color-accent-one | #FFC600 |
color-900-gray | #212121 |
| Token | Points to |
|---|---|
surface-primary | color-white |
surface-darker | color-900-gray |
border-primary-color | color-primary |
border-secondary-color | color-secondary |
text-primary | color-900-grayT |
text-light | color-whiteT |
text-link | color-global-linkT |
Key decisions
- Three tiers, one directionCore colors feed base tokens, and base tokens feed component tokens; type, spacing and radius follow the same pattern. No component token holds a raw hex value. The intended path is Core → Base → Component: 96 of 130 component color tokens follow it today, and 34 legacy tokens, set up before the base tier existed, still point straight at core.Trade-off: more tokens to name up front, in exchange for brand changes that live in the variables, not the components.
- Name by intent, not appearance
button-primary-default-fill, notcolor-900-gray. Intent names survive rebrands and give designers, engineers and AI-assisted tools the same vocabulary.Trade-off: a naming guide alone didn't stop drift without oversight. Renames are breaking changes, so each corrected version ships beside the old one until the old one is deprecated. - Keep the core layer out of reachBrand core colors and raw type and space values are hidden from Figma's pickers, so teams apply base and component tokens, and a carrier's palette changes in one place. Only an extended palette for charts and illustrations stays in reach.Trade-off: when a design needs a shade with no base token, the answer is a new base token rather than a quick pick.
Outcomes to date
- Completed
- A shared visual language, the tiered token architecture, and Figma component libraries for desktop and mobile web. The code library ships a default theme plus six carrier themes, switched with one attribute, and adding a carrier is a scripted step.
- In use
- Product teams and business units building for Zinnia, Security Benefit, Everly and four more carrier brands.
- Next
- Re-point the 34 legacy component tokens to base tokens, now that the Figma variable sync runs again (Case 02).
Explore the architecture and token examples
How the system is built
Bloom began as a component library and grew into the design system for the whole of Zinnia. I built the foundations first: typography, color and iconography as one visual language. On top of that I set up the tiered token architecture, and built the component libraries so components consume base tokens wherever a base role exists. The libraries cover two ranges: Zinnia Live is built for desktop widths from 500px to 1140px and up, and the consumer product supports phones down to 360px.
In numbers: 731 variables in 12 collections, 609 published to teams and 122 hidden primitives. 125 core colors feed 109 base tokens and 130 component tokens across 19 components, and 223 of those 239 color tokens carry a usage note. Type runs on 48 styles, spacing on a 4px scale, and layout on five breakpoints from 360px. Each carrier theme swaps 14 to 20 core colors and re-points only the base and component tokens its brand needs.
One component's tokens: the primary button
| State | Fill | Text | Rendered |
|---|---|---|---|
| Default | surface-darker | text-light | |
| Hover | surface-primary | text-primary | |
| Pressed | surface-quaternary | text-primary | |
| Selected | surface-tertiary | text-primary |
button-primary-{state}-fill and -text, pointing to a base role. The border stays border-darker in every state. The samples are rendered in HTML from Zinnia's token values.