Copyright © 2025 the Contributors to the Design Tokens Resolver Module Specification, published by the Design Tokens Community Group under the W3C Community Contributor License Agreement (CLA). A human-readable summary is available.
This specification extends the [format](../format/) and describes a method to work with alternate values for [design tokens](../), such as “light mode” and “dark mode” color themes for supporting devices.
This specification was published by the Design Tokens Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.
This is a snapshot of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C or the Design Tokens W3C Community Group Membership. Don’t cite this document other than as work in progress.
This document has been published to facilitate Wide Review.
This document was produced by the Design Tokens W3C Community Group, and contributions to this draft are governed by Community Contributor License Agreement (CLA), as specified by the W3C Community Group Process.
GitHub Issues are preferred for discussion of this specification.
This section is non-normative.
Expressing alternate values for design tokens multiples the number of values to manage for every layer. This specification describes an efficient way to work with alternate valueswhile producing the fewest minimal end number. We’ll compare a naïve approach to the resolver approach outlined in this document.
The naïve approach multiplies the number of final values flatly with the number of alternate value layers. Mathematically, this can be expresed as the original starting number of tokens 𝑇, multiplied by layers of alternate values 𝐴𝑉, produces a final number of tokens 𝑇𝛥:
The resolver approach involves breaking apart all tokens 𝑇 into subsets 𝑡1, 𝑡2, … 𝑡𝑛, and applying alternate value layers separately to produce a subtotal. The subtotals are added together to produce a final 𝑇𝛥 value. The key difference is avoiding flat multiplication across the entire superset by breaking into subsets. Mathematically this may be expressed like so:
This illustrates the concept in abstract. See syntax to see how it’s expressed in JSON.
The mechanism by which multiple possible values of design tokens are reduced to a single value, i.e. this module.
A subset of all design tokens that collectively form the default superset. Implementors SHOULD ensure the sum total of sets contain mutually exclusive tokens, i.e. they don’t overwrite one another and may be combined in any order to produce the same result.
A subset of all design tokens that provide alternate values. Modifiers MAY take an input to be used in providing alternate values.
The user’s selection for the modifiers, expressed as a key–value map. See example.
The property of tokens to express different values under different conditions. “Light mode” and “dark mode” are examples of alternate values.
The process of combining token sets and applying modifiers based on the specified inputs to produce the final set of tokens.
The characteristic of modifiers that do not overlap with one another, i.e. operate on different tokens. Modifiers MAY be orthogonal, but it are not required to be.
A resolver is a JSON object with the following properties:
Name | Type | Required | Description |
---|---|---|---|
name | string |
A short, human-readable name for the resolver. | |
description | string |
Additional information about the resolver’s purpose. | |
sets | Set[] | Y | Array of token subsets used as the base for resolution. |
modifiers | Modifier[] | Array of modifiers that may provide alternate values. |
A resolver MAY provide a human-readable name. This is used to identify the resolver.
A resolver MAY provide additional information.
A resolver MUST provide an array of sets that combine to form the minimum set of design tokens. A set is an object with the following properties:
Name | Type | Required | Description |
---|---|---|---|
name | string |
An optional identifier for this set. | |
values | <token-defs> | Y | The tokens that belong to this set. |
A resolver MAY provide an array of modifiers that extend, append, and/or override the final token values. A modifier is an object with the following properties:
Name | Type | Required | Description |
---|---|---|---|
name | string |
Y | The name of the modifier. |
type | "enumerated" | "include" |
The type of modifier (default: enumerated) |
An enumerated modifier adds the following additional properties:
Name | Type | Required | Description |
---|---|---|---|
values | Input[] |
Y | The inputs required for this modifier. |
meta | Object |
Additional data for this modifier | |
meta.default | string |
Declare the default value of the input, if none is provided. | |
meta.namespace | string |
Namespace all tokens with a valid token name. |
An enumerated modifier that allows users to omit an input value MUST specify a default value.
An include modifier adds the following additional properties:
Name | Type | Required | Description |
---|---|---|---|
values | Input[] |
Y | The inputs required for this modifier. |
meta | Object |
Additional data for this modifier |
This type of modifier is used to conditionally include a set of tokens. The values
array for an include modifier contains objects with a name
and a corresponding list of values
(file paths or inline tokens) that will be included if that name is present in the input.
An input is a mapping of modifier names to modifier values declared in any resolver. Inputs are not part of the resolver file itself, rather, provided to the tool alongside the resolver. A resolver that declares any modifiers MUST be consumed with an input as options.
An input SHOULD be serializable to a JSON object. Meaning, an input MAY be expressed in any programming language, but that expression should be easily converted back into a JSON object. Related concepts would include an object in JavaScript or a dictionary in Python.
Tools that load a resolver that declares modifiers SHOULD throw an error if an accompanying input is not provided.
Namespacing allows for automatic prefixing of token names
Example:
Modifiers are said to be orthogonal when they do not operate on the same set of tokens. In practical terms, if modifiers are orthogonal, then the order in which they are applied isn’t significant since they will produce the same values.
Implementors SHOULD make modifiers orthogonal. Tools MAY decide how to handle non-orthogonal modifiers.
An array consisting of:
string
which MUST be a valid URI pointing to a valid Tokens JSON file, orAny other inputs are invalid.
The array order is significant, where tokens with the same name overwrite previous instances of that token, if any.
An $extensions
object MAY be added to any set, modifier, or object in this spec to declare arbitrary metadata ignored by tooling. Its purpose in a resolver is the same as in the format.
Tools MUST handle the resolution stages in this order to produce the correct output.
Tools MUST require all inputs meet the schema described in that resolver’s modifiers syntax.
If a resolver does NOT declare any modifiers, skip this step and proceed to Sets flattening
Tools MUST iterate over the resolver’s sets syntax in order.
values
array to form the basis for all tokens.values
array, merging the objects together.values
array, repeating steps 2–3.values
have been merged into the basis object, keep that in memory and continue onto the next set.Conflict resolution occurs when flattening sets or applying modifiers, and a token name is occupied by tokens of different values, from different sources. In many cases, this is intentional, but not always.
When 2 tokens try and occupy the same space, tools MUST resolve the conflict in the following manner:
$type
is unknown), overwrite the value.Apply the selected modifiers based on the inputs. Modifiers can override tokens from the base sets or introduce new tokens.
modifiers
array, iterate in declaration order.meta.default
.meta.namespace
is declared.Alias resolution may only done after all sets and modifiers are handled, and there are no other tokens to merge in. Resolve aliases the same way as outlined in the format, allowing deep aliases but erring and stopping resolution on circular aliases and/or aliases that point to unresolvable types (such as aliasing a dimension token inside a gradient token, which is invalid).
After all aliases resolve correctly in the final set, the end result is one tokens object, that behaves as if it was a single JSON file to begin with.
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key words MAY, MUST, MUST NOT, and SHOULD in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
Tools implementing the Resolver Specification MUST:
This section is non-normative.
This resolver spec wouldn’t have happened without the Hyma Team, including but not limited to Mike Kamminga, Andrew L'Homme, and Lilith. Significant contributions were also made by Joren Broekema, Louis Chenais. We thank the members of the Design Tokens Community Group for their contributions and feedback.
Referenced in:
Referenced in: