Component-First vs Token-First Design Systems
There are two ways to start a design system, and the order you pick decides what kind of system you end up with.
You either start with components — Button, Card, Input — and pull tokens out later when you notice the same hex code in fifteen places. Or you start with tokens — color.bg.primary, space.4, radius.md — and build components by composing them.
It sounds like a small ordering question. It is not. The order leaks into everything: how teams theme, how they migrate, how designers and engineers talk to each other, how much code you ship to the browser.
Component-first: ship the box, figure out the contents later
A component-first system gives you a <Button variant="primary"> on day one. The library decides what “primary” looks like. Tokens, if they exist, live inside the component as defaults — sometimes exposed as theme overrides, sometimes baked in.
The classic examples:
- Material UI (MUI). The whole library is built around Material’s component vocabulary. There is a theme object with palette and typography, but the mental model is “I’m using
<Button>,<TextField>,<DataGrid>.” You override the theme to bend it; you don’t compose primitives from scratch. - Ant Design. Same shape. You import
<Table>and you get a fully formed table with sorting, filtering, pagination. The opinion is the product. - Bootstrap (the original).
.btn,.card,.navbar. Variables exist in the Sass, but most teams never touched them — they used the components. - Chakra UI (early versions). Component-first with a theme object. The newer versions have moved closer to a token model, but the DNA is component composition.
Designer-side, this maps to libraries like the old Material kit in Figma — pre-built components you drop in.
Pros
- Fastest possible start. A team can have a working app on Friday with twenty production-grade components.
- Visual consistency is automatic, because everyone is grabbing the same
<Button>. - Less decision fatigue. You don’t argue about radius scales — the radius is whatever MUI says it is.
- Accessibility comes for free if the library did its job.
<Dialog>already traps focus.
Cons
- Theming is shallow. You can change the primary color, but rebranding into something that isn’t a Material/Ant clone requires fighting the library.
- Lock-in. Migrating off MUI is a multi-quarter project because your component API is MUI’s API.
- Bundle bloat. You import
<Button>and a runtime, a theme provider, an emotion cache, and the kitchen sink come along. - Designers and engineers can drift. The Figma library says one thing; the code library says another. The component is the contract, and the contract has two sources of truth.
- The system stops where the library stops. Need a component MUI doesn’t ship? You’re now building outside the system, in the system’s house style, by hand.
Token-first: define the alphabet, then write words
A token-first system starts with the smallest atoms — color values, spacing increments, type ramps, radii, shadows, motion durations — and treats components as combinations of those atoms. The token layer is the contract. Components are downstream.
Token-first systems usually have at least two layers:
- Primitive tokens —
color.blue.500,space.4. Raw values. - Semantic tokens —
color.bg.surface,color.text.danger. Mapped to primitives, with intent.
Some add a third component token layer (button.bg.primary.hover) so a component’s surface area is itself a set of named knobs.
The clearest examples:
- Tailwind CSS. The config file is the design system.
theme.colors,theme.spacing,theme.fontSize. Components are HTML you compose. The library has no<Button>— you build one frombg-blue-600 px-4 py-2 rounded-md. - Radix Primitives + your own components. Radix gives you behavior (a headless
<Dialog>); you bring the tokens and the styling. The system you ship is yours, not Radix’s. - GitHub Primer. Tokens are published as a standalone package (
@primer/primitives) consumed by web, iOS, and the marketing site. Components are downstream consumers. - Shopify Polaris. Tokens are first-class —
--p-color-bg-surface,--p-space-400— and the React components consume them. You can use the tokens without the components. - Adobe Spectrum. Token-defined and platform-portable; the same scale shows up in Spectrum Web Components, Spectrum for React, and the native apps.
- Salesforce Lightning Design System. One of the earliest token-first systems at scale, with design tokens published as platform-agnostic JSON/YAML.
- IBM Carbon. Token layer (
@carbon/themes,@carbon/layout) is the source of truth; component packages are layered on top.
The Style Dictionary approach — Amazon’s tool for compiling one token source into CSS, iOS, Android, and Flutter outputs — is the canonical token-first pipeline.
Pros
- Theming is trivial. Light, dark, high-contrast, and per-brand themes are the same component swapping a different token map.
- Portability across platforms. iOS, Android, web, and email can all consume the same tokens, compiled to whatever format each platform needs.
- The component layer is replaceable. You can swap your React components for Vue, or rewrite from styled-components to CSS variables, without renegotiating the visual contract.
- Designers and engineers share a single language.
space.4means the same thing in Figma variables and in CSS. - Smaller surface area to govern. The token list is finite; the component list grows forever.
Cons
- Slow to start. On day one you have hex codes and a Tailwind config, not a working button. Someone has to build the components.
- Inconsistency without governance. If every team composes their own button, you get fifteen buttons. The token layer is consistent; the component layer drifts unless you also ship components or strict guidelines.
- Designers used to component libraries struggle. “Just use the tokens” is a harder ask than “drop in the Button.”
- Accessibility is now your job. There is no
<Dialog>doing focus management for you unless you bring Radix or Ariakit or write it yourself. - Token taxonomies get political. What counts as semantic? Should
color.bg.dangerexist or justcolor.bg.error? Bikeshedding scales with org size.
Which one to pick
The honest answer is that it depends on what you’re optimizing for and how long the system has to live.
Pick component-first when you have one product, one brand, one platform, and a small team that needs to ship now. The cost of customization later is real, but it’s a future cost. MUI on a B2B SaaS with a six-person frontend team is a very rational decision.
Pick token-first when any of these are true: you have multiple brands or themes, you ship to more than one platform, your design language is differentiated enough that an off-the-shelf library would fight you, or the system has to outlive any specific framework. The upfront tax is real, but you only pay it once.
The pattern I see most often in mature systems is a migration from one to the other — usually component-first to token-first. A team starts on MUI to ship, and three years later extracts a token layer underneath because they need dark mode, or a second brand, or want to migrate off MUI without breaking every screen. The token layer is what makes that migration possible.
If you can see that future from the start, you can save yourself the migration by going token-first on day one. If you can’t, component-first is fine — just don’t pretend the tokens you bolt on later are the same thing as a token layer designed first.