EduFlow

Section 2 · Design Tokens

The DTCG Token Format

20 min4 min readSpot the bug

Lesson at a glance

Read and write design tokens in the DTCG 2025.10 JSON format, with types, groups, aliases and deprecation, so one token file can feed Figma, CSS and native apps.

  1. 1Where the format stands in 2026
  2. 2Anatomy of a token file
  3. 3Types and value shapes
  4. 4Aliases
  5. 5Two more features worth knowing

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

CSS custom properties are a great way to consume tokens on the web, but they are a poor place to store them. The iOS app can’t read your stylesheet, Figma can’t import it, and a script that checks contrast across themes has to parse CSS to find the values. Northwind’s tokens live in JSON files instead, in the format defined by the Design Tokens Community Group (DTCG), and every platform’s code is generated from them.

Where the format stands in 2026

The DTCG is a W3C Community Group with members from design tool makers and large design system teams. On 28 October 2025 it published the first stable version of its specification, 2025.10, made of three modules: Format, Color, and a Resolver module for combining sets and themes.

Be precise when you describe it to others. It is a stable Community Group report, not a W3C Recommendation; Community Groups don’t produce formal web standards. In practice it is the format tools converge on: Style Dictionary, Tokens Studio and Terrazzo implement it, and Figma imports and exports variables as DTCG JSON. Support for the newer parts, such as the object color format and the Resolver module, still varies between tools, so check what your pipeline actually reads before adopting a feature.

Anatomy of a token file

tokens/primitives.tokens.json
{
  "color": {
    "$type": "color",
    "blue": {
      "600": {
        "$value": { "colorSpace": "srgb", "components": [0.122, 0.353, 0.878], "hex": "#1f5ae0" },
        "$description": "Northwind brand blue. Passes 4.5:1 on white."
      }
    },
    "white": {
      "$value": { "colorSpace": "srgb", "components": [1, 1, 1], "hex": "#ffffff" }
    }
  },
  "space": {
    "$type": "dimension",
    "4": { "$value": { "value": 1, "unit": "rem" } }
  }
}

Read it from the outside in:

  • Groups are plain nested objects (color, blue, space). They organize tokens and form the path used in references.
  • A token is any object with a $value. Here, color.blue.600 and space.4 are tokens.
  • $type declares what kind of value the token holds. Set on a group, it is inherited by every token inside, which is why color declares it once.
  • $description is free text that tools show in pickers and documentation.

Properties starting with $ are reserved for the format, so token and group names can’t start with $. Names also can’t contain {, } or ., because those characters belong to the reference syntax.

Types and value shapes

Each type has a fixed value shape. The ones you will use daily:

$type Example $value
color { "colorSpace": "srgb", "components": [1, 1, 1], "hex": "#ffffff" }
dimension { "value": 16, "unit": "px" } (unit is px or rem)
duration { "value": 150, "unit": "ms" } (unit is ms or s)
fontWeight 600 or "semi-bold"
number 1.5

There are also fontFamily and cubicBezier, plus composite types that bundle several values: typography, shadow, border, transition, gradient and strokeStyle.

The color object is new in 2025.10. Earlier drafts, and many token files in the wild, store a plain hex string. The object form can describe colors outside sRGB, such as display-p3 or oklch, which is why it replaced the string. Keep the optional hex for tools that only understand sRGB.

Aliases

The semantic tier from earlier lessons lives in the same format, as references:

tokens/semantic.tokens.json
{
  "color": {
    "$type": "color",
    "action": {
      "bg": { "$value": "{color.blue.600}" },
      "text": { "$value": "{color.white}" },
      "bg-hover": { "$value": "{color.blue.700}" },
      "bg-dark": {
        "$value": "{color.blue.700}",
        "$deprecated": "Use color.action.bg-hover instead. Removed in 5.0."
      }
    }
  }
}

A curly-brace reference names a token by its path with dots and resolves to that token’s whole $value. It must point at a token, never at a group. References can chain (semantic → primitive), and a tool must reject circular ones. For the rare case where you need one part of a value, such as a single color component, the format also supports JSON Pointer references with $ref.

Here is the core of what every token tool does with aliases:

JavaScript
const tokens = {
  color: {
    $type: 'color',
    blue: { 600: { $value: { colorSpace: 'srgb', components: [0.122, 0.353, 0.878], hex: '#1f5ae0' } } },
    action: {
      bg: { $value: '{color.blue.600}' },
      'bg-subtle': { $value: '{color.action.bg}' },
    },
  },
};

function lookup(path) {
  const node = path.split('.').reduce((n, key) => n?.[key], tokens);
  if (!node || !('$value' in node)) throw new Error(`{${path}} is not a token`);
  return node;
}

function resolve(path, seen = []) {
  if (seen.includes(path)) throw new Error(`circular reference: ${[...seen, path].join(' -> ')}`);
  const { $value } = lookup(path);
  const ref = typeof $value === 'string' && $value.match(/^\{(.+)\}$/);
  return ref ? resolve(ref[1], [...seen, path]) : $value;
}

console.log(resolve('color.action.bg-subtle').hex);
try { resolve('color.action'); } catch (e) { console.log(e.message); }

Two more features worth knowing

$deprecated, shown above on bg-dark (a name that describes a look, which the naming lesson warned against), marks a token as on its way out. Its value is true or a string explaining what to use instead, and tools can warn when someone picks it. You’ll build a deprecation process around it in Section 4.

Groups can also inherit from other groups with $extends, overriding only what differs. That is handy for a Tidewater group that reuses most of Northwind’s structure. For full light, dark and brand combinations, the Resolver module defines how separate token sets and modifiers combine, but it is the newest part of the specification, so Northwind still assembles themes in its build pipeline. The recommended file extensions are .tokens and .tokens.json; Northwind uses the second so editors highlight the files as JSON.

In the exercise below, you review a token file the way a pipeline would and find the lines it rejects. Next, you build that pipeline.

Key takeaways

  • The DTCG format stores tokens as JSON objects with a $value, and a $type set on the token or inherited from its group.
  • Aliases use curly braces with dot-separated paths, such as {color.blue.600}, and must point at a token, not a group.
  • Since 2025.10 a color value is an object with colorSpace and components, with an optional hex fallback.
  • Version 2025.10 is the first stable release of a W3C Community Group report, not a W3C Recommendation, and tool support still varies.

Further reading

Check your understanding

3 questions

Question 1 of 3

What does {color.action} resolve to in this file?

JSON
{
  "color": {
    "$type": "color",
    "action": {
      "bg": { "$value": "{color.blue.600}" },
      "text": { "$value": "{color.white}" }
    }
  }
}
Question 2 of 3

A token has no $type, and neither does any group above it. Its value is "#1f5ae0". What must a conforming tool do?

Question 3 of 3

A teammate says, “DTCG is a W3C standard now, so every tool reads every feature.” What’s the accurate correction?

Practice

Spot the bug

Find what the pipeline will reject

Northwind’s CI validates every token file against the DTCG 2025.10 format before building. This file fails. Select every line that a conforming tool would reject. Assume the rest of the file, including the color values, is correct.

Select the line(s) you think are wrong, then check.