Skip to content

Design Tokens With Style Dictionary: CSS, iOS, and Android

How Style Dictionary turns Figma design tokens into CSS, iOS, and Android values from one source, plus the DTCG token format and where teams trip up.

· · 14 min read

Updated: October 16, 2026

iMac screen showing a UI component library and design system spec

Design tokens solve a real problem: the same brand blue living as a different hex in three codebases until a rebrand turns into a week of find-and-replace. Style Dictionary is the tool that kills that problem. You define each token once, and it generates the CSS variable, the Swift constant, and the Android resource from a single source. After a product moves from hand-copied colors to a token pipeline, a rebrand becomes a change to a handful of source values instead of a week of find-and-replace. That's the whole pitch. The same pipeline is what makes running several brands off one system practical rather than theoretical, which we cover in multi-brand theming.

If tokens are new to you, start with the broader picture in the design tokens Figma to code guide. This one goes deeper on the specific tool most teams end up reaching for, and where it fits between Figma and your codebase.

TL;DR: Style Dictionary is an open-source build step that takes your design tokens from one JSON source and generates them for every platform: CSS variables, iOS Swift, Android XML. Figma variables feed it through an export, ideally in the DTCG token format (a W3C Community Group spec, not a ratified W3C standard). The tooling is easy. Naming discipline is what makes or breaks it.

A close-up of color swatch cards stacked into a single block
Photo by Peter Olexa on Unsplash

What Does Style Dictionary Actually Do?

Style Dictionary is an open-source build step. Its input is a set of token files, usually JSON. Its output is those same tokens in every format your platforms speak. One color/brand/primary value becomes a CSS custom property for web, a Swift constant for iOS, and an XML resource for Android, all generated, none hand-typed.

The magic word is transforms. A raw token might be #2563eb. A transform can turn that into rgb(), into a Swift UIColor, into an Android @color reference, whatever the target needs. You write the token once and configure how each platform should receive it. Change the source, run the build, and every platform moves together. No drift, no three-way copy-paste.

Photo by Markus Spiske on Unsplash

Define a token once, generate it everywhere. That single source of truth is what stops your platforms drifting apart.

Where Does Figma Fit In?

Designers don't write JSON, and they shouldn't have to. Figma variables are where tokens are born, though the two terms aren't quite interchangeable, see our Figma variables versus design tokens breakdown for exactly where that line sits. The connection to Style Dictionary is an export step: a plugin like Tokens Studio pushes your variables out to a JSON file, and it runs on any Figma plan. Figma also ships a Variables REST API, but there's a catch worth knowing before you architect around it: that endpoint needs a Full seat in an Enterprise organization, so on Professional or Organization plans it's simply unavailable and the plugin is your path.

That file, ideally in the DTCG token format that reached its first stable version (v2025.10) in October 2025, becomes Style Dictionary's input. Worth flagging: this is a W3C Community Group specification, a Community Group Report rather than a ratified W3C standard, so treat it as stable-but-evolving.

So the pipeline reads left to right: a designer edits a variable in Figma, exports to JSON, and Style Dictionary transforms it into platform code. The quality of the output depends entirely on the discipline of the input. Name your Figma variables semantically, color/surface, spacing/md, radius/sm, and the generated code is clean. Name them blue-3-final and you inherit that mess in every codebase. The token structure you build in the step-by-step design system guide is what makes this stage painless.

Here's how one Figma variable lands on each platform once it runs through the build:

Figma variableCSS outputiOS (Swift)Android (XML)
color/brand/primary--color-brand-primaryUIColor constant@color resource
spacing/md--spacing-mdSwift constantdimen resource
radius/sm--radius-smSwift constantdimen resource

One source value, three platform outputs, none of them hand-typed.

A person holding a printed sheet of solid color blocks up to the light
Photo by Helena Lopes on Unsplash

Why Get the Token Values Right First?

A pipeline only ships what you feed it, so the values matter as much as the tooling. Your spacing tokens should sit on a consistent scale, not arbitrary numbers, and our free spacing scale generator builds one you can paste straight in. Your type sizes should follow a ratio rather than guesswork, which is what the type scale calculator is for. Feed Style Dictionary a clean, systematic set of values and the output is a system. Feed it a pile of one-off numbers and you've just automated inconsistency.

Photo by Andy Brown on Unsplash

How Should You Tier Token Names: Primitive, Semantic, Component?

Use three tiers: primitive, semantic and component, each one referencing the tier below it instead of a raw value. Naming isn't one flat list, and the teams that treat it as one are the teams whose rebrand still takes a week.

TierAlso calledExamplePoints at
Primitiveoption, base, globalblue-500: #2563eba raw value
Semanticalias, decisioncolor-actiona primitive
Componentscopedbutton-bga semantic

The primitive layer is your palette: raw, literal, named after what it is. Nothing in a component should ever point at a primitive directly. The semantic layer is where meaning lives, color-action, color-danger, surface-raised, and it's the layer a theme actually swaps. Component tokens are optional. Add them only when a component needs to diverge from the semantic default, not by reflex for every button and card.

Why three tiers and not two? Because the semantic layer is the thing that lets one primitive change ripple everywhere while a component override stays local. Skip it and you're back to blue-500 hard-coded across forty components. Over-build the component tier and you drown in card-header-title-color-hover tokens nobody can find six months later. My rule: always ship primitive and semantic, and earn the component tier case by case.

How Do You Automate the Export So It Stays Honest?

Push the variables to a Git branch from Figma, build in CI, and open a pull request instead of committing to main. "Wire the export into a script" is easy to say and it's where most token pipelines quietly die, so here's what the wiring actually looks like.

The shape that survives contact with a real team has three moving parts. Tokens Studio pushes your variables to a Git branch directly from Figma, so the designer's save is already a commit. A CI job on that branch runs the Style Dictionary build and writes the generated CSS, Swift and XML into the repo. Then the job opens a pull request instead of pushing to main.

That last step is the one people skip, and it's the one that matters. A token change that arrives as a PR gets a diff, a reviewer and a history. You can see that color-action moved from #2563eb to #1d4ed8 and that it touched fourteen components. Push straight to main and the same change lands as an unattributed hex swap somebody notices three weeks later on a staging build.

Two details worth pinning down before you build this. Decide whether the generated output is committed or built at install time, and commit it if any consumer outside your repo eats those files, because a downstream team should not need your toolchain to get a variable. And make the CI job fail loudly when the token JSON does not parse, rather than emitting a half-built stylesheet. A pipeline that degrades silently is worse than a manual export, because at least the manual one has somebody watching it.

My opinion, and it does cost something: run the build in CI even when a single developer could run it locally in four seconds. The four seconds were never the point. The reviewable diff is.

Is There a Standard Token Format Yet?

Yes. The first stable version, 2025.10, shipped in October 2025, and it's further along than the old "draft" label suggests.

The Design Tokens Community Group format, DTCG, is the W3C effort to make a token file mean the same thing in every tool that reads it. It's a small spec. A token is an object with a $value and a $type, optionally a $description, and groups nest by plain JSON nesting. That's most of it.

{
  "color": {
    "action": { "$value": "{color.blue.500}", "$type": "color" }
  }
}

Why care about a format when your pipeline already works? Because the format is what decides whether you can leave. Tokens written in one vendor's shape are a migration project the day that vendor changes its pricing or its plugin stops being maintained. Tokens written in DTCG are a file that the next tool can read.

The catch is honest: the spec is stable now but young, and tools implement slightly different slices of it. Style Dictionary's own documentation says the 2025.10 format is not fully supported yet and that the work is in progress for v5. Aliasing syntax and composite types like typography and shadow are where you'll find the gaps. Style Dictionary and Tokens Studio both speak it, which covers the common path from this article, but do not assume two DTCG exporters produce byte-identical files.

I'd still write DTCG today. A young standard that three tools read beats a proprietary shape that one tool reads, and the migration cost only goes up with the size of your token set.

How Do Light, Dark and Brand Modes Become Output Files?

Run one build per mode over the same primitives, with one semantic file per mode. A token set with one answer per name is a toy; real systems answer the same question several times: what is color-action in light, in dark, in the acquired company's palette? Figma models that as modes on a variable collection, and the ceiling depends on your plan. Since Schema 2025, Professional allows up to 10 modes per collection and Organization up to 20. Enterprise adds extended collections, where a central team publishes a core system and brand teams extend it with their own themes while still inheriting every upstream change.

Style Dictionary has no mode concept of its own, and that turns out to be fine. The pattern that holds up is boring: one file of primitives, one semantic file per mode, and one build per mode over the same primitives.

import StyleDictionary from "style-dictionary";

const modes = {
  light: ":root",
  dark: '[data-theme="dark"]',
};

for (const [mode, selector] of Object.entries(modes)) {
  const sd = new StyleDictionary({
    source: ["tokens/primitives.json", `tokens/semantic.${mode}.json`],
    platforms: {
      css: {
        transformGroup: "css",
        buildPath: "build/css/",
        files: [
          {
            destination: `tokens.${mode}.css`,
            format: "css/variables",
            filter: (token) => token.filePath.includes("semantic"),
            options: { selector },
          },
        ],
      },
    },
  });
  await sd.buildAllPlatforms();
}

Two options carry the weight. selector on the css/variables format decides where the values land, so light writes to :root and dark writes to [data-theme="dark"]. The filter keeps the primitives out of the mode files; without it every mode would repeat the whole palette. Run against Style Dictionary 5.5.5 with color.action pointing at blue.500 in the light file and blue.300 in the dark one, this writes --color-action: #2563eb to the first stylesheet and --color-action: #93b4f5 to the second. Components keep reading var(--color-action) and never learn which mode they are in. A brand is the same move with a different attribute, hand-written in plain CSS in the multi-brand piece linked in the intro.

The standard has an answer of its own now. The 2025.10 release added a Resolver module. A .resolver.json file declares token sets and modifiers; a modifier called theme lists contexts such as light and dark, each pointing at its own token files, and a resolutionOrder decides what wins. Ask for { "theme": "dark" } and you get the dark set. Is that what you should build on today? Not yet, because Style Dictionary's docs flag full 2025.10 support as in progress. Write your files in DTCG now and the day a tool reads the resolver natively, you migrate one config file, not the tokens.

Who Gets to Change a Token, and How Does One Get Retired?

Give every token tier an owner and a review step, and retire tokens with $deprecated instead of deleting them. A token set turns into a public API the moment a second team consumes it, and an API without rules ends in a quiet breakage nobody can trace.

Three rules cover most of it.

  • Owners by tier. One small group owns the primitive and semantic tiers, because those are the ones every product reads. The team that owns a component owns its component tokens. Without a named owner, "somebody should fix that" is the whole process.
  • Changes sorted by blast radius. Adding a token is cheap. Changing a value shows up in every consumer at the next build. Renaming or removing one breaks code. Treat that last kind as a breaking release and ship it with a migration note; the versioning piece linked at the end of this guide covers how.
  • Retire before you remove. The DTCG 2025.10 format lets a token or a whole group carry $deprecated, set to true or to a string saying why. On a group it applies to every child token unless one sets it to false.
{
  "color": {
    "action-primary": {
      "$value": "{color.blue.500}",
      "$type": "color",
      "$deprecated": "Use color.action. Removed in the next major."
    }
  }
}

The field is the contract; it isn't enforcement. Whether your tooling warns on it varies, so add a CI check that fails the build when a token name breaks your naming pattern or when new code references a deprecated one. Why a check and not a style guide? Memory is a poor governance system, and a build that goes red is a good one.

The spec itself says nothing about who may delete a token. That part is yours.

Where Do Teams Trip Up?

Three snags come up again and again.

Photo by Jakub Zerdzicki on Unsplash
  • Naming that leaks implementation. Tokens named after their value (blue-500) instead of their role (color-action) can't survive a theme change. Name by intent.
  • Skipping the semantic layer. Reference base tokens through semantic ones, so color-button-bg points at color-brand-primary. Then a rebrand changes one base value and cascades everywhere.
  • Treating the export as one-and-done. Figma variables change. If the export is a manual chore nobody remembers, the code drifts from the design again. Wire the export into a script so it stays honest.

One upside once the pipeline runs: point Style Dictionary at a TypeScript output and every token ships as a typed constant your editor autocompletes. The typed output pays off in daily work, too. Reference a token that doesn't exist and the compiler flags it before the component ever renders, so a renamed color-action-primary can't quietly rot half your buttons.

That safety net is exactly why teams bother generating typed tokens instead of loose strings in the first place. Large systems generate thousands of them, and that used to drag type-checking down. Since TypeScript 7 (Project Corsa) reached GA in July 2026, the native Go compiler checks that generated surface roughly 10x faster, so a big token file no longer taxes the build. Coding Dunia's TypeScript 7 guide breaks down what actually changed.

Design tokens are also the cleanest thing you can hand a developer, which is why they sit at the heart of a good designer to developer handoff. Get the tokens right in Figma, let Style Dictionary do the translating, and the gap between what you designed and what ships mostly closes on its own. My take: the tooling is the easy part. The naming discipline is the whole game, and it's the part most teams shortchange.

Once a second team consumes your tokens, changing a value becomes a release question rather than a design one; that side of it is in versioning and changelogs.

Frequently Asked Questions

What is Style Dictionary and why use it?
Style Dictionary is an open-source build tool (originally created at Amazon, now maintained together with Tokens Studio) that takes design tokens in a single source file and outputs them in whatever formats your platforms need: CSS custom properties, SCSS variables, iOS Swift, Android XML, JSON, and more. The point is one source of truth. You define color/brand/primary once, and Style Dictionary generates the CSS variable, the Swift constant, and the Android resource from it. Change the token, rebuild, and every platform updates together. Without it, teams hand-copy the same hex into three codebases and drift apart within a sprint.
How do Figma variables connect to Style Dictionary?
Figma variables are where designers define tokens; Style Dictionary is where those tokens become code. The bridge is an export. A plugin like Tokens Studio exports your variables to a JSON file (this works on any Figma plan). There's also a Figma Variables REST API, but heads up: reading or writing variables through it needs a Full seat in an Enterprise org, so most teams on Professional or Organization plans reach for the plugin route instead. That JSON, ideally in the DTCG token format, becomes Style Dictionary's input. So the flow is: designer edits a variable in Figma, exports to JSON, Style Dictionary transforms it into platform code. The cleaner your Figma variable naming, the cleaner the generated output, which is why naming discipline in Figma pays off downstream.
Do I need Style Dictionary for a small project?
Not always. If you ship one web app and nothing else, plain CSS custom properties exported from Figma may be enough, and adding a build pipeline is overhead. Style Dictionary earns its place the moment you have more than one platform, more than one output format, or a token set large enough that hand-syncing becomes error-prone. For a solo web project, start simple. For a design system serving web, iOS, and Android, it saves you from a maintenance nightmare that only gets worse with scale.