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.
- 1Where the format stands in 2026
- 2Anatomy of a token file
- 3Types and value shapes
- 4Aliases
- 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
{
"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.600andspace.4are tokens. $typedeclares what kind of value the token holds. Set on a group, it is inherited by every token inside, which is whycolordeclares it once.$descriptionis 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:
{
"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:
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$typeset 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
colorSpaceandcomponents, with an optionalhexfallback. - Version 2025.10 is the first stable release of a W3C Community Group report, not a W3C Recommendation, and tool support still varies.