A design token is the least glamorous artifact in modern frontend work and one of the most consequential. It is a named value, --color-blue-500 or --space-md, that stands in for a raw number so that the number lives in exactly one place. The idea is old enough to have an origin story: the term, as the Design Tokens Community Group notes, was created by the Salesforce design system team, credited to Jina and Jon, who needed a word for the indivisible pieces of a system. A decade later, the public design systems that everyone borrows from, GitHub Primer, Atlassian, Shopify Polaris, have converged on a strikingly similar token architecture. The convergence is real. So are the disagreements it papers over, and the disagreements are where the useful lessons hide.
What “design token” became
The historical frame is Brad Frost’s Atomic Design, the 2016 book that taught a generation to think of interfaces as atoms, molecules, organisms, templates, and pages. Tokens are the subatomic layer beneath that, the foundational decisions about color, spacing, and type that everything else composes from. Frost himself now sells a course called “Subatomic” devoted entirely to them, which is a fair signal of how the field matured: the granular, boring layer turned out to be the load-bearing one.
The maturation produced a standard. The DTCG’s Design Tokens Format Module reached its first stable version, 2025.10, in October 2025, defining a vendor-neutral JSON interchange format where every token has a $value, a $type, and an optional $description, and tokens can alias other tokens with curly-brace references like {color.blue-500} that get resolved at build time. The point of the standard is portability: a token file should move between Figma, Style Dictionary, and Tailwind without a rewrite. The membership list tells you the stakes, since Adobe, Amazon, Atlassian, Figma, Google, and Microsoft all sit on the group. Tokens stopped being a private convention and became infrastructure with a spec.
Three public systems, one architecture
Look at three mature systems and the same three-tier structure appears, under different names.
GitHub Primer is the most explicit. Its token naming convention defines exactly three categories. Base tokens are the lowest level and map directly to a raw value, like base-color-green-5. Functional tokens represent global UI patterns, like bgColor-inset or borderColor-default. Component tokens are scoped to a single component and belong only in that component’s CSS, like button-primary-bgColor-hover. The values ship as JSON in the @primer/primitives npm package, compiled to CSS variables with Style Dictionary, and Primer’s internal tokens guide states the governing rule bluntly: never use raw values, only semantic tokens.
Atlassian arrives at the same place from the color side. Per Atlassian Design, every color token starts with the word color, followed by the property it applies to, background or border or icon, then modifiers for color role, emphasis level, and interaction state. The roles are a fixed semantic vocabulary: neutral, brand, information, success, warning, danger, discovery, accent. That is the middle tier, the semantic layer, expressed as intent rather than appearance. Salesforce, the system that gave the field its name, built the same primitive-then-semantic stack. Three teams, three naming schemes, one shape: raw values at the bottom, named intentions in the middle, component-specific overrides at the top.
The semantic-versus-primitive disagreement
Here is the first place the convergence cracks. Everyone agrees the tiers exist. They disagree about which tier you are allowed to touch, and that disagreement has real cost.
The purist position, which Primer enforces, is that product code should reference only the semantic and component tiers, never a primitive. You write var(--fgColor-default), never var(--base-color-gray-9). The reason is durability, and the canonical horror story makes it concrete. The designer Manan Desai described a product whose primary button referenced --color-blue-500 directly. When the brand color changed from blue to teal, that single rename broke more than ninety components, because the components had bound themselves to the appearance of the color rather than its role. A semantic token, --color-primary, would have absorbed the change in one place. The whole point of the middle tier is to be the seam where a rebrand happens without touching component code.
The cost of the purist position is verbosity and a steeper learning curve. Every value now requires a naming decision and at least two indirections from raw number to rendered pixel. Small teams routinely find this overhead absurd for a five-component product, and they are not wrong for their scale. The disagreement is not about whether semantic tokens are correct in the abstract. It is about when the indirection starts paying for itself, and the answer is genuinely a function of how many components share the value and how likely the value is to change.
The whole point of the middle tier is to be the seam where a rebrand happens without touching component code.
The theming disagreement
The second crack is theming, and it is where the systems diverge in mechanism, not just naming. Atlassian’s approach is to make tokens reactive to a host theme: a Forge app calls view.theme.enable() and its colors mirror whatever theme Jira or Confluence is running, switching live when the parent switches. The token is a promise that resolves differently depending on context. Primer ships the same idea as a matrix of theme files, with separate compiled CSS for light, dark, dark-dimmed, and a row of accessibility variants, high-contrast, colorblind, tritanopia, applied through data-color-mode and data-theme attributes on the document.
The disagreement underneath is about how many axes a token has to flex along. A system that only needs light and dark can encode theme as a simple attribute swap. A system like Primer that ships nine-plus color themes, including dedicated colorblind and high-contrast variants tied to WCAG AA contrast ratios of 4.5:1 for text and 3:1 for UI, needs the semantic layer to carry far more weight, because every semantic token must resolve correctly in every theme. The more themes you support, the more the semantic tier is doing, and the less optional it becomes. Theming is the strongest argument for the middle tier that small teams keep trying to skip.
Where Tailwind sits
Tailwind is the system most teams actually use, and it occupies an interesting position in this taxonomy. With Tailwind v4, released in early 2026, the framework went CSS-first, and its theme documentation now says the quiet part directly: the low-level design decisions Tailwind stores in theme variables “are often called design tokens.” You define one with the @theme directive, --color-mint-500: oklch(0.72 0.11 178), and Tailwind does two things at once: it generates the utility classes bg-mint-500 and text-mint-500, and it emits a plain CSS variable you can reference anywhere. That dual nature is the tell. Tailwind collapses the primitive tier and the utility layer into one declaration.
What Tailwind largely omits, by design, is the semantic middle tier. Out of the box, bg-blue-500 is a primitive utility, not a semantic intention, which is why teams that adopt Tailwind for a serious design system end up adding their own semantic layer on top, defining --color-primary in @theme and pointing the brand utilities at it. Tailwind gives you the bottom tier and the tooling for free and leaves the architecture decision to you. It is a token system with the opinions removed, which is exactly why it is popular and exactly why it does not, on its own, save you from the rebrand-breaks-ninety-components problem.
Practical guidance for teams without a design-systems staff
The lesson for a team that has no dedicated design-systems engineer is not to copy Primer’s full apparatus. It is to copy the part of the convergence that pays back early and skip the part that does not. Three moves are enough.
First, establish the semantic tier even if you skip the rest. You do not need nine themes or a component-token layer, but you do need --color-primary and --bgColor-default pointing at your primitives, so the rebrand has a seam. This is the single move that prevents the most expensive failure, and it costs an afternoon.
Second, name for intent, not appearance, in that semantic tier, which is the rule every public system enforces and Desai learned the hard way. --color-danger, never --color-red-500, anywhere a component touches it. Keep the raw red-500 as a primitive that only the semantic token references.
Third, if you are on Tailwind v4, do this work inside @theme rather than fighting the framework, defining primitives and semantic aliases together and pointing your utilities at the semantic names. If you are not, adopt the DTCG JSON format and compile with Style Dictionary, because the 2025.10 standard means your tokens will survive a tooling change.
The thing the public systems quietly agree on, underneath all three disagreements, is that the token layer is not a styling convenience. It is the place where a design decision gets a single home, and the entire value of the system is that there is exactly one place to change when the decision changes. Primer says never use raw values for a reason. The teams that skip the semantic tier are not saving work. They are deferring it to the day the brand color changes, when the bill comes due across every component at once.