Token Compiler Comparison: Choosing a Build Tool

Part of Token Scaling, Validation & CI Pipelines. This page frames the token compiler selection decision as an architectural choice — not a preference — because the tool you commit to determines your output formats, theming model, extensibility ceiling, and migration cost for years.

Choosing the wrong compiler creates compounding debt: custom transforms that fight the tool’s native model, multi-platform output that requires manual post-processing, and a pipeline that breaks every time the upstream tool releases a major version. The goal here is to give you enough signal to make an informed call on day one, and a migration path if you’re already on the wrong tool.

The Decision Surface

Token Compiler Decision Flow A decision flowchart guiding selection between Style Dictionary, Theo, and Cobalt based on team needs for multi-platform output, DTCG format support, and maintenance status. Start: Pick a Compiler What's your primary constraint? Multi-platform output needed? (iOS, Android, CSS, JS, SCSS…) Yes No / CSS-only Style Dictionary Mature, extensible, multi-platform DTCG format required? (W3C spec compliance) Yes No / Salesforce stack Cobalt (cobalt-ui) DTCG-native, modern output Theo Salesforce-origin, maintenance risk
Token compiler decision flow: multi-platform need routes to Style Dictionary; DTCG compliance need routes to Cobalt; Salesforce-stack / CSS-only may use Theo with caveats.

Problem Framing

The failure mode that lands teams in compiler debt looks the same everywhere: a developer picks a tool based on a blog post, wires it into CI, and six months later tries to add iOS Swift output or a second brand theme. The tool either can’t do it natively or requires a transform chain complex enough that it would have been faster to switch tools at the start.

A concrete example: Theo’s format model is flat — it expects a single global.yml or global.json and outputs per-format files. Reasonable for one brand, but when a second brand needs a different set of primitive values, Theo has no native concept of multi-set merging. Teams end up writing a pre-processor to generate multiple Theo inputs and a post-processor to merge outputs, which is exactly the kind of glue code that becomes unmaintainable when Theo’s own transforms change.

The DTCG format compounds this. If your design tooling exports W3C DTCG JSON (the $value / $type schema), you need a compiler that understands it natively. Neither Theo nor Style Dictionary v3 parsed DTCG out of the box — only Cobalt and Style Dictionary v4 do. Picking a tool without confirming DTCG support means writing a converter layer before compilation even begins.

Feature Comparison

Feature Style Dictionary v4 Theo (Salesforce) Cobalt (cobalt-ui)
Format support JSON, YAML, DTCG (v4), custom parsers JSON, YAML, SCSS maps DTCG-native; JSON5, YAML via plugins
Transforms Composable transform chains; 30+ built-ins Fixed set; limited composability Plugin-based; typed transform pipeline
Multi-platform output CSS, SCSS, JS, TS, iOS Swift, Android XML, Compose CSS, SCSS, JS, Sass variables, Common JS CSS vars, JS/TS, JSON, Tailwind config
Theming / multi-brand Sets + references + token filtering Manual file splitting Sets API; first-class theme layering
Extensibility Custom parsers, transforms, formats, actions Plugin API (limited docs) Plugin API; TypeScript-typed
Maintenance status Active; Amazon + community; v4 stable Low-activity; last release 2023 Active; small team; growing adoption
DTCG compliance v4 full; v3 requires converter None native Full; spec-tracking
Best for Enterprise multi-platform, iOS/Android teams Legacy Salesforce Lightning codebases CSS-first, modern W3C toolchains

The DTCG Format

The W3C Design Tokens Community Group specification defines a canonical JSON schema for design tokens. A DTCG file uses $value and $type keys instead of bare value, and supports composite types like typography and shadow as structured objects rather than concatenated strings.

{
  "color": {
    "action": {
      "primary": {
        "$type": "color",
        "$value": "#2563eb",
        "$description": "Primary interactive surface — buttons, links, focus rings"
      }
    }
  }
}

If your Figma Tokens or Tokens Studio export produces this format, you need either Cobalt or Style Dictionary v4. Feeding a DTCG file to Style Dictionary v3 or Theo produces silent mismatches — the $value key is not recognized as the token value, so you get empty or undefined output for every token. This failure is hard to catch without schema validation, because the build completes without errors. Pairing your compiler with JSON Schema Validation for Tokens catches this class of mismatch before compilation runs.

Three-Tier Architectural Trade-Offs

Style Dictionary: ceiling vs. onboarding cost

  • High extensibility ceiling; every output format, parser, and transform is replaceable — but the config surface is large and the v3→v4 API break is non-trivial.
  • Native iOS and Android output makes it the only viable choice for native mobile teams; competitors require hand-rolling platform formatters.

Theo: simplicity vs. longevity

  • Minimal API means a new team member can understand the full pipeline in an afternoon; but the maintenance trajectory means you are likely inheriting a frozen tool.
  • YAML support is a genuine advantage for teams that find JSON token files unwieldy; but it does not compensate for lack of DTCG support or theming primitives.

Cobalt: spec alignment vs. ecosystem maturity

  • First-class DTCG means zero converter code for modern Figma toolchains; the trade-off is a smaller plugin ecosystem and fewer production references.
  • The TypeScript-typed plugin API reduces transform bugs; but a small core team means slower response to breaking changes in upstream DTCG spec drafts.

Evaluation Workflow

Follow this sequence before committing to a compiler. Each step produces a concrete artifact that either confirms the tool or rules it out.

  1. Inventory your token sources. List every format currently produced by your design tooling (Figma Tokens, Tokens Studio, Specify, hand-authored JSON). Identify whether the format is DTCG, legacy Theo-style flat JSON, or nested Style Dictionary JSON.

  2. Map required output platforms. Enumerate every consumer: CSS custom properties for web, Swift/ObjC constants for iOS, XML resources for Android, Compose tokens for Android Compose, Tailwind config, Sass variables, JavaScript constants for React Native. Any non-web platform routes you toward Style Dictionary.

  3. Prototype each candidate tool’s build step. Take a 20-token sample (one color ramp, one spacing scale) and wire it through each candidate. Measure: does it parse your source format without conversion? Does the output match what consumers expect?

  4. Stress-test theming. Create two brand sets with conflicting primitive values. Verify the tool can produce two independent CSS output files from a shared token base without manual output merging.

  5. Check CI integration. Run the build step in a GitHub Actions runner with --no-install (using the cache). Verify exit codes are reliable: 0 on success, non-zero on transform errors. Tools that swallow errors and exit 0 silently corrupt your CI signal.

  6. Evaluate extensibility ceiling. Write a custom transform: take a color token and output an additional hsl() alias variable. If the tool’s API makes this harder than writing 30 lines of JavaScript, that friction will compound on every future custom requirement.

  7. Assess migration risk. Read the tool’s changelog for the past 18 months. Count breaking changes, deprecations, and open issues against your required features. A tool with infrequent releases is either extremely stable or abandoned — determine which from issue activity, not release dates alone.

Style Dictionary v4 in Depth

Style Dictionary is the reference implementation for production token compilers. Its transform pipeline is composable: you register named transforms, group them into transform groups, and assign groups to platforms.

// style-dictionary.config.mjs
import StyleDictionary from 'style-dictionary';

const sd = new StyleDictionary({
  source: ['tokens/**/*.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      buildPath: 'dist/css/',
      files: [{
        destination: 'tokens.css',
        format: 'css/variables',
        options: {
          outputReferences: true,
          selector: ':root'
        }
      }]
    },
    ios: {
      transformGroup: 'ios-swift',
      buildPath: 'dist/ios/',
      files: [{
        destination: 'StyleTokens.swift',
        format: 'ios-swift/class.swift',
        className: 'StyleTokens'
      }]
    },
    android: {
      transformGroup: 'android',
      buildPath: 'dist/android/',
      files: [{
        destination: 'tokens.xml',
        format: 'android/resources'
      }]
    }
  }
});

await sd.buildAllPlatforms();

The outputReferences: true option is architecturally significant: it preserves alias relationships in the CSS output as var() references rather than resolved values. This is what enables runtime theming — when --color-action-primary references --color-blue-600, overriding the primitive at the theme layer automatically updates all semantic usages. The token model in Token Fundamentals & Naming Conventions explains why this matters for the primitive→semantic→component hierarchy.

Cobalt in Depth

Cobalt’s primary differentiator is that it reads DTCG JSON directly, with no configuration layer between your source file and the parser. The build config is minimal:

// cobalt.config.js
export default {
  tokens: './tokens.json', // DTCG-formatted
  outDir: './dist',
  plugins: [
    pluginCSS({
      filename: 'tokens.css',
      selector: ':root',
      modeSelectors: {
        'dark': ['[data-theme="dark"]'],
        'light': ['[data-theme="light"]']
      }
    }),
    pluginJS({
      filename: 'tokens.js',
      export: 'default'
    })
  ]
};

The modeSelectors API is Cobalt’s theming primitive. You declare named modes in your DTCG source, then map each mode to a CSS selector. The plugin emits scoped CSS blocks automatically. This is the right architecture for a CSS-first, multi-theme system — but it requires that your design tooling emits DTCG with mode annotations. Review Design-to-Code Sync Workflows for how to configure Tokens Studio to emit this format on export.

Theo in Depth

Theo accepts a flat JSON or YAML source and emits format-specific files. The mental model is simple: one input format, many output formats via explicit format specifiers.

# tokens/global.yml
props:
  color_action_primary:
    value: "#2563eb"
    type: color
    category: color
  spacing_base:
    value: "8px"
    type: size
    category: spacing

global:
  category: color
  type: color
// theo.build.js
const theo = require('theo');

theo
  .convert({
    transform: { type: 'web' },
    format: { type: 'css' }
  })
  .then((css) => {
    fs.writeFileSync('dist/tokens.css', css);
  });

Theo’s flat structure is readable but limiting. There is no reference resolution: if color_action_primary should reference a primitive color_blue_600, you must resolve that relationship yourself before passing the file to Theo. This means your CI pipeline carries a pre-processor step that Style Dictionary and Cobalt handle natively.

The more significant concern is maintenance. The npm package has had no active development since mid-2023. For new systems, treat Theo as a legacy option only if you are already inside the Salesforce Lightning ecosystem and cannot migrate.

CI Integration

The following GitHub Actions workflow runs a Style Dictionary v4 build. The same structure applies to Cobalt with node cobalt.config.js replacing style-dictionary build, and to Theo with its build script.

# .github/workflows/build-tokens.yml
name: Build Design Tokens
on:
  push:
    paths:
      - 'tokens/**'
  pull_request:
    paths:
      - 'tokens/**'

jobs:
  build-tokens:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci --ignore-scripts

      - name: Validate token schema
        run: |
          npx ajv validate \
            --spec=draft2020 \
            -s schemas/tokens.schema.json \
            -d tokens/tokens.json

      - name: Build tokens
        run: npx style-dictionary build --config style-dictionary.config.mjs

      - name: Verify output artifacts
        run: |
          test -f dist/css/tokens.css || (echo "CSS output missing" && exit 1)
          test -f dist/ios/StyleTokens.swift || (echo "iOS output missing" && exit 1)
          grep -c 'var(--' dist/css/tokens.css | \
            awk '$1 < 10 {print "Suspiciously few var() references"; exit 1}'

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: token-artifacts
          path: dist/
          retention-days: 30

The schema validation step runs before the build — it catches malformed DTCG or naming convention violations before they produce confusing compile errors. The verify step after the build catches a common silent failure: Style Dictionary will exit 0 even when a malformed config produces zero tokens in the output. The artifact upload gives you a downloadable build for every PR, which is essential for reviewing generated Swift or XML files that cannot be easily diffed in a browser.

Migration Cost Between Tools

Migration is rarely zero-cost, but it is also rarely as expensive as teams fear. The table below assumes you are migrating a token set of ~200–500 tokens.

Migration Path Effort Key Work Automation Potential
Theo → Style Dictionary v4 Medium (2–4 days) Restructure flat props into SD nested JSON; write reference aliases High — scripted with jq or Node
Theo → Cobalt Medium-High (3–5 days) Convert to DTCG $value/$type format; map Theo categories to DTCG types Medium — requires type mapping table
Style Dictionary v3 → v4 Low-Medium (1–3 days) Update config to ESM; migrate removed APIs (dictionary.allPropertiesdictionary.allTokens) High — v4 migration guide covers most cases
Style Dictionary v4 → Cobalt Medium (2–4 days) Convert SD JSON to DTCG; replace platform configs with Cobalt plugins Medium — source JSON is structurally similar
Cobalt → Style Dictionary v4 Low (1–2 days) SD v4 reads DTCG natively; rewrite plugin configs as SD platform configs High

The highest-risk migration is any tool → Cobalt when your token source is not yet DTCG. The DTCG conversion is where effort concentrates: you must correctly assign $type to every token (not just color — also dimension, fontFamily, fontWeight, duration, cubicBezier, shadow, typography, border, gradient), and composite types require restructuring flat string values into objects.

For Theo → Style Dictionary migrations specifically, the deep comparison of Style Dictionary vs. Theo vs. Cobalt covers the conversion scripts and alias resolution strategy in detail.

Cross-Cluster Dependency Mapping

Parent / Sibling Integration Point Validation Strategy
Token Scaling & CI Pipelines (parent) Compiler output is the artifact CI distributes Exit code validation + artifact size check
JSON Schema Validation for Tokens Schema runs before compiler; catches format mismatches ajv validate in pre-build step
Design-to-Code Sync Workflows Compiler sits between Figma export and CSS artifact delivery Format compatibility check on webhook trigger
Token Fundamentals & Naming Conventions Compiler enforces naming via transform filters Custom transform rejects tokens violating [tier]-[domain]-[role] pattern

Diagnostic Matrix

Diagnostic Step Execution Detail
Build exits 0 but CSS file is empty Run with --verbose; check that source glob resolves (npx style-dictionary build --verbose 2>&1 | grep "source")
DTCG tokens appear as undefined in output Confirm compiler version supports DTCG; SD v3 silently ignores $value — upgrade to v4
Alias references not resolved Check circular reference chain with sd.hasCircularReferences(); Cobalt surfaces these as build errors automatically
Swift/Android output missing Verify platform config key matches transformGroup name exactly; SD transform groups are case-sensitive
Theo build silently drops tokens Theo requires category field; tokens without it are excluded without warning — validate input against Theo’s schema manually

Root Causes and Resolutions

Empty output after SD v3 → v4 migration. Style Dictionary v4 uses ES modules by default. If your sd.config.js uses module.exports, the config is silently ignored. Rename to style-dictionary.config.mjs and use export default.

DTCG composite tokens produce string output instead of structured values. Cobalt and SD v4 both require that composite token $value fields use JSON objects, not concatenated strings. A shadow token with $value: "0 2px 4px rgba(0,0,0,0.1)" is technically invalid DTCG — it must be a structured object with offsetX, offsetY, blur, spread, and color keys.

Alias chain breaks at brand theme boundary. When two brand token files both define color.brand.primary and are merged before compilation, the last-writer-wins merge loses the first brand’s value. Use Style Dictionary’s Sets API or Cobalt’s modeSelectors to keep brand definitions namespaced rather than merging them.

Theo YAML produces wrong CSS variable names. Theo converts underscores in prop names to hyphens in CSS output: color_action_primary becomes --color-action-primary. If your codebase uses a different naming convention, Theo’s name transform is not overridable without forking the package.

Frequently Asked Questions

Which tool should I pick for a new design system from scratch?

Start with Style Dictionary v4 if you have any native mobile platforms or expect to add them. Start with Cobalt if you are CSS-first, your Figma tooling already exports DTCG, and you have no iOS/Android requirement. Avoid starting with Theo for new systems — the maintenance risk is too high to take on voluntarily.

Can I run two compilers in parallel during migration?

Yes, and this is the recommended migration strategy. Run the legacy tool on your existing token source and the target tool on the same source in parallel, diffing outputs. Once outputs are equivalent (allowing for expected naming differences), remove the legacy tool. The CI snippet above can be adapted to run both build steps in a matrix job.

Does Style Dictionary v4 fully support DTCG?

Yes, with one caveat: the v4 parser handles the $value/$type schema and DTCG composite types. However, some edge cases in the spec (particularly gradient and typography composite tokens) require explicit format handlers if your output format does not natively understand the composite structure. Check the SD v4 migration guide and the DTCG conformance test suite before declaring full compliance.

What about tools like Specify, Supernova, or Knapsack?

These are token management platforms, not compilers. They typically use Style Dictionary or a proprietary compiler under the hood for the build step. When evaluating them, ask what they use internally and what format they export — most either export DTCG or Style Dictionary-compatible JSON, which means the compiler decision still applies downstream.