From 30de46cd226deca41b486640b1443f58b28a369d Mon Sep 17 00:00:00 2001 From: Mike Kamminga <988169+mikekamminga@users.noreply.github.com> Date: Tue, 29 Jul 2025 13:32:09 +0200 Subject: [PATCH 1/9] Adding technical report: Resolver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds the Resolver specification working document which was orginally created by the team at Hyma/Tokens Studio as a contribution to the DTCG working group and the evolution of Design Tokens standards and ecosystem. All comments from the source Google doc — where earlier collaration found place — which were open/unresolved have been included in this version as ISSUE blocks throughout the document and need to be discussed and resolved. --- technical-reports/resolver/CHANGELOG.md | 50 + technical-reports/resolver/index.html | 1627 +++++++++++++++++++++++ 2 files changed, 1677 insertions(+) create mode 100644 technical-reports/resolver/CHANGELOG.md create mode 100644 technical-reports/resolver/index.html diff --git a/technical-reports/resolver/CHANGELOG.md b/technical-reports/resolver/CHANGELOG.md new file mode 100644 index 0000000..a447c3e --- /dev/null +++ b/technical-reports/resolver/CHANGELOG.md @@ -0,0 +1,50 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [2.1.0] - 2025-07-23 + +This release incorporates editorial improvements and community feedback summarized from the [Design Tokens Resolvers Specification Working Copy](https://docs.google.com/document/d/1LOtdiS8R903R7RwDd22JiDxljh51l7Xfy9M1D-p-9mU/edit?tab=t.0#heading=h.svkctwfaregs), identifying key areas for future specification development. + +### Added + +- **Resolution Aliasing Section:** Added a complete new section explaining aliasing/namespacing concepts with detailed examples and JSON Schema definition (from Working Copy) +- **Community Feedback Integration:** Incorporated editorial comments and issues from the working copy document throughout the specification, highlighting ambiguities, inconsistencies, and areas needing clarification. + +### Issues Identified (from Working Copy) + +- **Terminology Clarifications:** Highlighted need for better definitions of "process", "inputs", "dimensions" vs "contexts", and disambiguation between different types of aliasing. +- **File Extension Recommendations:** Suggested using `.tokens.json` extension to align with Design Tokens Format Specification conventions. +- **Schema Structure Improvements:** Identified need to move functional properties out of generic `meta` property into formal schema definitions. +- **Modifier Structure Concerns:** Raised questions about using arrays vs objects for modifiers to ensure uniqueness and prevent conflicts. +- **Merging Logic Specification:** Highlighted need for detailed DTCG-compliant merging algorithms and conflict resolution rules. +- **Precedence and Order:** Identified need for explicit precedence rules when multiple modifiers affect the same tokens. +- **Orthogonality Declaration:** Suggested need for explicit orthogonality declarations to support lazy resolution. + +### Notes + +- This version focuses on integrating community feedback and issue identification from the working copy rather than normative specification changes. +- Issues summarized from the working copy will inform future specification development and clarifications. + +## [2.0.0] - 2023-10-27 + +This is the first major revision of the specification based on a detailed technical review. The goal of this release is to add clarity, address ambiguities, and provide a more robust foundation for implementers. + +### Added + +- **"Include" Modifier Type:** Added a new `include` type for modifiers, which is used to conditionally include a set of tokens. An example has been added to the "Modifiers" section. +- **Order of Precedence:** A new subsection, "Order of Precedence," has been added to the "Resolution Logic" section to explicitly define the merge order for base sets and modifiers. +- **Error Handling Guidance:** A new informative section, "Error Handling," has been added to recommend specific error types for common failure scenarios (e.g., `FileNotFoundError`, `CircularReferenceError`). + +### Changed + +- **Modifier Type:** The `type` property on modifiers now defaults to `"enumerated"`. +- **Inline Token Definitions:** Clarified that an "inline token definition" must be a complete JSON object representing a valid token structure. An example has been added to the "Token Sets" section. +- **Final Output Format:** The specification now explicitly states that the final resolved output should be a nested JSON object that mirrors the token paths, as shown in the examples. +- **Path Resolution:** It is now explicitly stated that file paths in a resolver file must be resolved relative to the location of the resolver file itself. +- **Alias Resolution Scope:** The spec now clarifies that alias resolution is performed on the fully merged set of tokens, allowing aliases to reference tokens across any loaded file. +- **`meta.alias` Behavior:** The behavior of `meta.alias` is now more clearly defined, explaining that it namespaces the tokens from the modifier's files and that external references must use this namespace. + +### Fixed + +- **Inconsistent `\$value` Key:** Corrected all instances of an inconsistent `value` key in JSON examples to use `\$value`, aligning with the Design Tokens Format Specification. \ No newline at end of file diff --git a/technical-reports/resolver/index.html b/technical-reports/resolver/index.html new file mode 100644 index 0000000..0637abe --- /dev/null +++ b/technical-reports/resolver/index.html @@ -0,0 +1,1627 @@ + + +
+ ++ This document describes the technical specification for a resolver mechanism used to manage and resolve design tokens in complex scenarios involving themes, modes, brands, and other modifiers. It extends the Design Tokens Format Specification by introducing a structured way to combine and override token sets to produce a final, resolved set of tokens for consumption by design tools and platforms. +
++ 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. +
++ Design tokens are key-value pairs that represent design decisions, allowing for consistent use across different platforms and tools. In complex design systems, there is a need to manage multiple dimensions such as themes, modes (e.g., light and dark), brands, and variants. This introduces challenges in organizing tokens, resolving references, and applying overrides. +
++ The Resolver Specification addresses these challenges by introducing a structured mechanism to: +
+In design systems, especially those at scale, managing multiple themes, modes, brands, and other modifiers can become complex. Designers and developers often struggle with:
+Existing approaches, such as embedding modifiers directly into token names or using global theme files, have limitations. They often lead to:
+The Resolver Specification aims to:
++ The term "process" needs clarification - what specific process is being referred to? Is this the "resolution process of the resolver"? More explanation is needed to understand exactly what becomes faster and more efficient when token scope is reduced during resolution. +
++ A side effect of using a resolver should be the ability to have a clear dependency graph of how a resolution request would occur purely based on the inputs. This would enable better understanding of modifier interactions and support more efficient resolution strategies. +
+ ++ The term "Dimension" is potentially confusing since "Dimension" is also a type of token and a general concept. Consider using "Context" instead, which aligns with the existing definition as "contexts in which token values might change." Additionally, explicit mention of color schemes may be problematic - developers often distinguish between "themes" and "color modes" (like Windows High Contrast mode for accessibility), and it's unclear whether everyone would understand themes to encompass dark/light/high-contrast modes. +
++ The modifier types and their behaviors aren't clear from these definitions alone. Real-world examples showing how Enumerated Modifiers, Include Modifiers, and Alias properties work in practice would help clarify their purpose and usage. +
++ The specification should address different real-world resolution use cases and their performance implications: full upfront resolution (resolving all possible combinations ahead of time), lazy resolution (resolving on-demand), and partial resolution for complex components. Without being upfront about token combinations and scopes, systems face combinatorial explosion problems that make resolution inefficient. There may be significant performance aspects to consider, especially for JIT (Just-In-Time) resolution scenarios. Input from tool makers like Figma and other design platforms would be valuable to understand real-world performance requirements and constraints. +
+A resolver is defined as a JSON object with the following properties:
+Each token set in the sets array is an object with the following properties:
+Example:
++{ + "sets": [ + { + "name": "foundation", + "values": ["foundation.json"] + }, + { + "values": [ + "components/button.json", + { + "inline-token": { "$value": "some-value" } + } + ] + } + ] +} ++
+ It is recommended to use .tokens.json
as the file extension for token files to align with the Design Tokens Format Specification naming conventions. This helps reinforce that these files should contain valid DTCG token structures rather than arbitrary JSON data.
+
Modifiers are defined in the modifiers array and can have different types, affecting how they influence the resolution process.
+Each modifier is an object with the following properties:
+
+ Default values and aliasing should be moved out of the generic meta
property into specific typed properties. The meta
property should be reserved for implementation-specific extensions rather than functionality that directly contributes to resolution behavior.
+
Example of an "enumerated" modifier:
++{ + "modifiers": [ + { + "name": "theme", + "type": "enumerated", + "values": [ + { + "name": "light", + "values": ["themes/light.json"] + }, + { + "name": "dark", + "values": ["themes/dark.json"] + } + ], + "meta": { + "default": "light", + "alias": "theme" + } + } + ] +} ++
+ There are structural problems with using arrays for modifiers and their values. Since name
is required and must be unique for proper resolution, arrays allow for multiple conflicting values which should be impossible. For example, if a user passes { theme: "dark" }
and there are 2 modifiers named "theme" and 2 modifier values named "dark", this creates ambiguity that should be an error rather than defaulting to "take the first one." While arrays make sense for defining order of application, if we want key-value mapping for applying modifiers, flat objects should be required to ensure unique IDs by design.
+
+ The specification needs to clarify what constitutes valid values in the values
array for modifiers. Key questions include: Are only relative pathnames allowed? Can referenced files have sets/modifiers of their own (enabling resolver chaining)? Should there be support for referencing sets by name (e.g., "foundation") rather than repeating file paths? The concept of chaining resolvers could use different type values to identify other resolvers. Additionally, values could support local references starting with "#" or string references to set names, making it easier to reference entire sets without listing all token file paths.
+
Example of an "include" 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.
++{ + "modifiers": [ + { + "name": "features", + "type": "include", + "values": [ + { + "name": "experimental-feature-x", + "values": ["features/feature-x.json"] + } + ] + } + ] +} ++
Aliasing allows for dynamic namespacing or renaming of token paths during resolution. This is particularly useful when integrating external token sets or avoiding naming conflicts.
+Namespace vs Alias Terminology: Should the alias
property be renamed to namespace
to avoid confusion with token aliases (references to other tokens)?
Redundancy with Modifier Names: The meta.alias
property may be redundant since modifiers already have a name
property that could serve the same namespacing purpose.
Auto-namespacing Concerns: The automatic application of namespacing for modifiers introduces significant complexity and potential issues:
+Alternative Approach: Consider removing auto-namespacing for modifiers and using shared merging logic between sets and modifiers for better compatibility and simplicity.
+Example:
+Given a token set size.json:
++{ + "sm": { + "value": "1px", + "type": "dimension" + }, + "lg": { + "value": "10px", + "type": "dimension" + } +} ++
By applying an alias in the modifier's meta.alias, we can namespace these tokens:
++{ + "modifiers": [ + { + "name": "size", + "type": "include", + "values": [ + { + "name": "default", + "values": ["size.json"] + } + ], + "meta": { + "alias": "spacing" + } + } + ] +} ++
Resulting in tokens accessible via spacing.sm and spacing.lg.
+
+ If the meta.alias
behavior described above is normative/required behavior, it should not be part of the generic meta
property but should be defined as part of the formal schema. Alternatively, if this is just an example of tooling-specific behavior, it should be clearly called out as such and not presented as part of the core specification.
+
Source: https://resolver-spec.netlify.app/reference/schema/
++{ + "$id": "https://schemas.tokens.studio/prototype/resolver.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Resolver Specification", + "$defs": { + "tokenSet": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["values"] + }, + "modifier": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["name", "values"] + } + }, + "meta": { + "type": "object", + "additionalProperties": true + } + }, + "required": ["name", "values"] + } + }, + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "sets": { + "type": "array", + "items": { + "$ref": "#/$defs/tokenSet" + } + }, + "modifiers": { + "type": "array", + "items": { + "$ref": "#/$defs/modifier" + } + } + }, + "required": ["sets", "modifiers"] +} ++
The resolution process involves the following steps:
++ The term "inputs" is used throughout the specification but never formally defined. Inputs appear to represent the permutation or combination of modifiers being selected for resolution - essentially the question "What would my output look like given some combination of modifiers being enabled or chosen?" This fundamental concept should be clearly defined, potentially as "data" or with a more specific term that clarifies its role in the resolution process. +
+
+ The merging process needs detailed specification to handle DTCG token data correctly. Simply performing a deep merge on raw JSON could yield different results than fully interpreting the DTCG data first and then merging. This is particularly important for inheritable format properties like $type
. For example, when loading two JSON files with a top-level "color" key where one has $type: "color"
and another has $type: "gradient"
, these represent incompatible schemas that cannot be merged. The specification must outline what constitutes "mergeable" vs "unmergeable" token sets and provide clear rules for handling such conflicts.
+
+ The specification needs clear step-by-step instructions for how deep merging should occur, including detailed guidance on handling edge cases or when to error appropriately. This should include specific algorithms and decision trees for implementers to follow consistently. +
++ The specification should clarify the resolution order when multiple modifiers are applied simultaneously. For instance, if both "theme" and "brand" modifiers are used and both attempt to override the same token, which modifier should take precedence? Clear precedence rules are needed to ensure consistent and predictable resolution behavior across implementations. +
++ The specification needs to clarify the different types of "aliasing" and their behavior during resolution. There are two distinct concepts being referred to as "aliasing": (1) Namespacing aliasing - where tokens are renamed/namespaced (e.g., "red.500" becomes "colors.red.500"), and (2) Token reference aliasing - where one token's value references another token's value. The specification should clearly distinguish between these concepts and address how namespacing aliases behave when tokens are overridden by later sets - specifically, if an alias points to a token that gets overridden, does the alias resolve to the new/overridden value? +
+Alias resolution is performed on the fully merged set of tokens, after all base sets and modifiers have been applied. This allows for aliases to reference tokens from any loaded file.
+We need to decide if the resolver spec also follows the $name, $values, etc.
++{ + "name": "Example Resolver", + "sets": [ + { + "name": "foundation", + "values": ["foundation.json"] + }, + { + "values": ["components/button.json"] + } + ], + "modifiers": [ + { + "name": "theme", + "type": "enumerated", + "values": [ + { + "name": "light", + "values": ["themes/light.json"] + }, + { + "name": "dark", + "values": ["themes/dark.json"] + } + ], + "meta": { + "default": "light", + "alias": "theme" + } + } + ] +} ++
+ The alias
property should be renamed to namespace
to make its purpose clearer. Additionally, it should be moved out of the generic meta
property and included in the formal schema definition. Currently, any arbitrary value could exist associated with alias
since it's within the unstructured meta
object. Anything that is not 100% discardable should not live in meta
.
+
+{ + "theme": "dark" +} ++
+{ + "color": { + "brand": { + "primary": { + "$value": "#FF0000", + "$type": "color" + } + } + } +} ++
+{ + "button": { + "background": { + "$value": "{theme.accent}", + "$type": "color" + }, + "padding": { + "$value": "8px", + "$type": "dimension" + } + } +} ++
+{ + "accent": { + "$value": "#00FF00", + "$type": "color" + } +} ++
Merge (flatten) tokens, resulting in:
++{ + "color": { + "brand": { + "primary": { + "$value": "#FF0000", + "$type": "color" + } + } + }, + "button": { + "background": { + "$value": "{theme.accent}", + "$type": "color" + }, + "padding": { + "$value": "8px", + "$type": "dimension" + } + } +} ++
Apply aliasing as per meta.alias ("theme"), resulting in:
++{ + "theme": { + "accent": { + "$value": "#00FF00", + "$type": "color" + } + } +} ++
Alias resolution is performed on the fully merged set of tokens, after all base sets and modifiers have been applied. This allows for aliases to reference tokens from any loaded file.
++{ + "color": { + "brand": { + "primary": { + "$value": "#FF0000", + "$type": "color" + } + } + }, + "button": { + "background": { + "$value": "#00FF00", + "$type": "color" + }, + "padding": { + "$value": "8px", + "$type": "dimension" + } + } +} ++
The Resolver Specification can be integrated with tools like Style Dictionary to automate the generation of platform-specific design tokens.
+Example:
+In large design systems, you might have multiple brands, each with light and dark themes, and accessibility modes (e.g., high contrast).
+Implementation:
+Modifiers are considered orthogonal when they can be changed independently without affecting each other's resolution logic.
+Example of Orthogonal Modifiers:
+You can mix any theme with any brand, resulting in all possible combinations.
+Example of Non-Orthogonal Modifiers:
+In such cases, the resolver must handle dependencies between modifiers, potentially by validating acceptable combinations or structuring modifiers to reflect the dependencies.
++ This requires input from the resolver author to explicitly declare whether a modifier is purely orthogonal or not. This declaration should be upfront in the specification, otherwise lazy resolution cannot be supported without the resolver having to check the actual tokens in scope. +
+The Resolver Specification can be extended to handle more complex scenarios:
+Tools implementing the Resolver Specification MUST:
+We thank the members of the Design Tokens Community Group for their contributions and feedback, including:
+This section is non-normative and intended for early reviewers.
+Aliasing allows for dynamic namespacing or renaming of token paths during resolution. This is particularly useful when integrating external token sets or avoiding naming conflicts.
+Example:
+Given a token set size.json:
++{ + "sm": { + "value": "1px", + "type": "dimension" + }, + "lg": { + "value": "10px", + "type": "dimension" + } +} ++
By applying an alias in the modifier's meta.alias, we can namespace these tokens:
++{ + "modifiers": [ + { + "name": "size", + "type": "include", + "values": [ + { + "name": "default", + "values": ["size.json"] + } + ], + "meta": { + "alias": "spacing" + } + } + ] +} ++
Resulting in tokens accessible via spacing.sm and spacing.lg.
+The GitHub Primer design system uses multiple dimensions, including themes and visual modes (e.g., colorblind modes). The Resolver Specification can represent these dimensions as modifiers, allowing for efficient resolution and management of tokens.
++ Question: Would it be worth to highlight some public design systems and how they would use the resolver spec for more relatable use cases? +
++ While public design system examples could be valuable, generic use cases showing common dimensional patterns might be more important as foundational examples: single dimension (1 brand), two dimensions (1 brand + 2 themes), three dimensions (2 brands + 2 themes each), etc. These generic patterns would be the "meat and potatoes" compared to specific design system examples being the "cherry on the cake." +
+In scenarios where modifiers are not orthogonal, the resolver may need to enforce acceptable combinations and handle dependencies between modifiers. This can be achieved by:
+Scenario: A company has multiple brands—Brand A and Brand B. Each brand has its own color palette and typography. Components like buttons and headers need to adapt based on the selected brand.
++ These examples could be moved to the Resolver Spec netlify app for better presentation. If it uses Astro, interactive code tabs could be added to make the examples more engaging and easier to understand through hands-on exploration. +
++{ + "name": "Brand Theming Resolver", + "sets": [ + { + "name": "base", + "values": ["tokens/base.json"] + }, + { + "values": ["tokens/components.json"] + } + ], + "modifiers": [ + { + "name": "brand", + "type": "enumerated", + "values": [ + { + "name": "brandA", + "values": ["tokens/brands/brandA.json"] + }, + { + "name": "brandB", + "values": ["tokens/brands/brandB.json"] + } + ], + "meta": { + "default": "brandA", + "alias": "brand" + } + } + ] +} ++
+{ + "color": { + "text": { + "primary": { + "value": "#000000", + "type": "color" + } + } + }, + "font": { + "family": { + "default": { + "value": "Arial, sans-serif", + "type": "font" + } + } + } +} ++
+{ + "button": { + "background": { + "value": "{brand.color.primary}", + "type": "color" + }, + "fontFamily": { + "value": "{brand.font.family}", + "type": "font" + } + }, + "header": { + "color": { + "value": "{brand.color.secondary}", + "type": "color" + } + } +} ++
+{ + "color": { + "primary": { + "value": "#FF5733", + "type": "color" + }, + "secondary": { + "value": "#C70039", + "type": "color" + } + }, + "font": { + "family": { + "value": "'Helvetica Neue', sans-serif", + "type": "font" + } + } +} ++
+{ + "color": { + "primary": { + "value": "#1F618D", + "type": "color" + }, + "secondary": { + "value": "#2874A6", + "type": "color" + } + }, + "font": { + "family": { + "value": "'Times New Roman', serif", + "type": "font" + } + } +} ++
+{ + "brand": "brandB" +} ++
Load tokens/base.json and tokens/components.json.
+Merge tokens:
++{ + "color": { + "text": { + "primary": { + "value": "#000000", + "type": "color" + } + } + }, + "font": { + "family": { + "default": { + "value": "Arial, sans-serif", + "type": "font" + } + } + }, + "button": { + "background": { + "value": "{brand.color.primary}", + "type": "color" + }, + "fontFamily": { + "value": "{brand.font.family}", + "type": "font" + } + }, + "header": { + "color": { + "value": "{brand.color.secondary}", + "type": "color" + } + } +} ++
Apply the "brand" modifier with value "brandB".
+Load tokens/brands/brandB.json.
+Apply aliasing as per meta.alias ("brand"), resulting in:
++{ + "brand": { + "color": { + "primary": { + "value": "#1F618D", + "type": "color" + }, + "secondary": { + "value": "#2874A6", + "type": "color" + } + }, + "font": { + "family": { + "value": "'Times New Roman', serif", + "type": "font" + } + } + } +} ++
No conflicts in this example.
++{ + "color": { + "text": { + "primary": { + "value": "#000000", + "type": "color" + } + } + }, + "font": { + "family": { + "default": { + "value": "Arial, sans-serif", + "type": "font" + } + } + }, + "button": { + "background": { + "value": "#1F618D", + "type": "color" + }, + "fontFamily": { + "value": "'Times New Roman', serif", + "type": "font" + } + }, + "header": { + "color": { + "value": "#2874A6", + "type": "color" + } + } +} ++
Scenario: A design system includes buttons that change appearance based on their state—default, hover, active, and disabled. We want to manage these state-specific styles using modifiers.
++{ + "name": "Component States Resolver", + "sets": [ + { + "values": ["tokens/components/button.json"] + } + ], + "modifiers": [ + { + "name": "state", + "type": "enumerated", + "values": [ + { + "name": "default", + "values": ["tokens/states/default.json"] + }, + { + "name": "hover", + "values": ["tokens/states/hover.json"] + }, + { + "name": "active", + "values": ["tokens/states/active.json"] + }, + { + "name": "disabled", + "values": ["tokens/states/disabled.json"] + } + ], + "meta": { + "default": "default" + } + } + ] +} ++
+{ + "button": { + "background": { + "value": "{state.background}", + "type": "color" + }, + "textColor": { + "value": "{state.textColor}", + "type": "color" + }, + "borderColor": { + "value": "{state.borderColor}", + "type": "color" + } + } +} ++
+{ + "background": { + "value": "#FFFFFF", + "type": "color" + }, + "textColor": { + "value": "#000000", + "type": "color" + }, + "borderColor": { + "value": "#CCCCCC", + "type": "color" + } +} ++
+{ + "background": { + "value": "#F0F0F0", + "type": "color" + }, + "textColor": { + "value": "#000000", + "type": "color" + }, + "borderColor": { + "value": "#BBBBBB", + "type": "color" + } +} ++
+{ + "background": { + "value": "#E0E0E0", + "type": "color" + }, + "textColor": { + "value": "#000000", + "type": "color" + }, + "borderColor": { + "value": "#AAAAAA", + "type": "color" + } +} ++
+{ + "background": { + "value": "#F9F9F9", + "type": "color" + }, + "textColor": { + "value": "#777777", + "type": "color" + }, + "borderColor": { + "value": "#DDDDDD", + "type": "color" + } +} ++
+{ + "state": "hover" +} ++
Load tokens/components/button.json.
+Tokens:
++{ + "button": { + "background": { + "value": "{state.background}", + "type": "color" + }, + "textColor": { + "value": "{state.textColor}", + "type": "color" + }, + "borderColor": { + "value": "{state.borderColor}", + "type": "color" + } + } +} ++
Apply the "state" modifier with value "hover".
+Load tokens/states/hover.json.
+Tokens under the "state" namespace:
++{ + "state": { + "background": { + "value": "#F0F0F0", + "type": "color" + }, + "textColor": { + "value": "#000000", + "type": "color" + }, + "borderColor": { + "value": "#BBBBBB", + "type": "color" + } + } +} ++
No conflicts in this example.
++{ + "button": { + "background": { + "value": "#F0F0F0", + "type": "color" + }, + "textColor": { + "value": "#000000", + "type": "color" + }, + "borderColor": { + "value": "#BBBBBB", + "type": "color" + } + } +} ++
Scenario: A design system needs to support responsive design by adjusting spacing and typography based on screen sizes—mobile, tablet, and desktop. We want to manage these variations using modifiers.
++{ + "name": "Responsive Design Resolver", + "sets": [ + { + "name": "core", + "values": ["tokens/core.json"] + } + ], + "modifiers": [ + { + "name": "screenSize", + "type": "enumerated", + "values": [ + { + "name": "mobile", + "values": ["tokens/screens/mobile.json"] + }, + { + "name": "tablet", + "values": ["tokens/screens/tablet.json"] + }, + { + "name": "desktop", + "values": ["tokens/screens/desktop.json"] + } + ], + "meta": { + "default": "mobile", + "alias": "screen" + } + } + ] +} ++
+{ + "spacing": { + "small": { + "value": "{screen.spacing.small}", + "type": "dimension" + }, + "medium": { + "value": "{screen.spacing.medium}", + "type": "dimension" + }, + "large": { + "value": "{screen.spacing.large}", + "type": "dimension" + } + }, + "typography": { + "fontSize": { + "value": "{screen.typography.fontSize}", + "type": "dimension" + } + } +} ++
+{ + "spacing": { + "small": { + "value": "4px", + "type": "dimension" + }, + "medium": { + "value": "8px", + "type": "dimension" + }, + "large": { + "value": "12px", + "type": "dimension" + } + }, + "typography": { + "fontSize": { + "value": "14px", + "type": "dimension" + } + } +} ++
+{ + "spacing": { + "small": { + "value": "6px", + "type": "dimension" + }, + "medium": { + "value": "12px", + "type": "dimension" + }, + "large": { + "value": "18px", + "type": "dimension" + } + }, + "typography": { + "fontSize": { + "value": "16px", + "type": "dimension" + } + } +} ++
+{ + "spacing": { + "small": { + "value": "8px", + "type": "dimension" + }, + "medium": { + "value": "16px", + "type": "dimension" + }, + "large": { + "value": "24px", + "type": "dimension" + } + }, + "typography": { + "fontSize": { + "value": "18px", + "type": "dimension" + } + } +} ++
+{ + "screenSize": "desktop" +} ++
Load tokens/core.json.
+Tokens:
++{ + "spacing": { + "small": { + "value": "{screen.spacing.small}", + "type": "dimension" + }, + "medium": { + "value": "{screen.spacing.medium}", + "type": "dimension" + }, + "large": { + "value": "{screen.spacing.large}", + "type": "dimension" + } + }, + "typography": { + "fontSize": { + "value": "{screen.typography.fontSize}", + "type": "dimension" + } + } +} ++
Apply the "screenSize" modifier with value "desktop".
+Load tokens/screens/desktop.json.
+Apply aliasing as per meta.alias ("screen"), resulting in:
++{ + "screen": { + "spacing": { + "small": { + "value": "8px", + "type": "dimension" + }, + "medium": { + "value": "16px", + "type": "dimension" + }, + "large": { + "value": "24px", + "type": "dimension" + } + }, + "typography": { + "fontSize": { + "value": "18px", + "type": "dimension" + } + } + } +} ++
No conflicts in this example.
++{ + "spacing": { + "small": { + "value": "8px", + "type": "dimension" + }, + "medium": { + "value": "16px", + "type": "dimension" + }, + "large": { + "value": "24px", + "type": "dimension" + } + }, + "typography": { + "fontSize": { + "value": "18px", + "type": "dimension" + } + } +} ++
- This document describes the technical specification for a resolver mechanism used to manage and resolve design tokens in complex scenarios involving themes, modes, brands, and other modifiers. It extends the Design Tokens Format Specification by introducing a structured way to combine and override token sets to produce a final, resolved set of tokens for consumption by design tools and platforms. + This document describes the technical specification for a resolver + mechanism used to manage and resolve design tokens in complex scenarios + involving themes, modes, brands, and other modifiers. It extends the + Design Tokens Format Specification by introducing a structured way to + combine and override token sets to produce a final, resolved set of + tokens for consumption by design tools and platforms.
- 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 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. + 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.
- Design tokens are key-value pairs that represent design decisions, allowing for consistent use across different platforms and tools. In complex design systems, there is a need to manage multiple dimensions such as themes, modes (e.g., light and dark), brands, and variants. This introduces challenges in organizing tokens, resolving references, and applying overrides. -
+ + + + + + + + + + + + + + + + + + + + + + +- The Resolver Specification addresses these challenges by introducing a structured mechanism to: -
-In design systems, especially those at scale, managing multiple themes, modes, brands, and other modifiers can become complex. Designers and developers often struggle with:
-Existing approaches, such as embedding modifiers directly into token names or using global theme files, have limitations. They often lead to:
-The Resolver Specification aims to:
-- The term "process" needs clarification - what specific process is being referred to? Is this the "resolution process of the resolver"? More explanation is needed to understand exactly what becomes faster and more efficient when token scope is reduced during resolution. -
-- A side effect of using a resolver should be the ability to have a clear dependency graph of how a resolution request would occur purely based on the inputs. This would enable better understanding of modifier interactions and support more efficient resolution strategies. -
- -- The term "Dimension" is potentially confusing since "Dimension" is also a type of token and a general concept. Consider using "Context" instead, which aligns with the existing definition as "contexts in which token values might change." Additionally, explicit mention of color schemes may be problematic - developers often distinguish between "themes" and "color modes" (like Windows High Contrast mode for accessibility), and it's unclear whether everyone would understand themes to encompass dark/light/high-contrast modes. -
-- The modifier types and their behaviors aren't clear from these definitions alone. Real-world examples showing how Enumerated Modifiers, Include Modifiers, and Alias properties work in practice would help clarify their purpose and usage. -
-- The specification should address different real-world resolution use cases and their performance implications: full upfront resolution (resolving all possible combinations ahead of time), lazy resolution (resolving on-demand), and partial resolution for complex components. Without being upfront about token combinations and scopes, systems face combinatorial explosion problems that make resolution inefficient. There may be significant performance aspects to consider, especially for JIT (Just-In-Time) resolution scenarios. Input from tool makers like Figma and other design platforms would be valuable to understand real-world performance requirements and constraints. -
-A resolver is defined as a JSON object with the following properties:
-Each token set in the sets array is an object with the following properties:
-Example:
--{ - "sets": [ - { - "name": "foundation", - "values": ["foundation.json"] - }, - { - "values": [ - "components/button.json", - { - "inline-token": { "$value": "some-value" } - } - ] - } - ] -} --
- It is recommended to use .tokens.json
as the file extension for token files to align with the Design Tokens Format Specification naming conventions. This helps reinforce that these files should contain valid DTCG token structures rather than arbitrary JSON data.
-
Modifiers are defined in the modifiers array and can have different types, affecting how they influence the resolution process.
-Each modifier is an object with the following properties:
-
- Default values and aliasing should be moved out of the generic meta
property into specific typed properties. The meta
property should be reserved for implementation-specific extensions rather than functionality that directly contributes to resolution behavior.
-
Example of an "enumerated" modifier:
--{ - "modifiers": [ - { - "name": "theme", - "type": "enumerated", - "values": [ - { - "name": "light", - "values": ["themes/light.json"] - }, - { - "name": "dark", - "values": ["themes/dark.json"] - } - ], - "meta": { - "default": "light", - "alias": "theme" - } - } - ] -} --
- There are structural problems with using arrays for modifiers and their values. Since name
is required and must be unique for proper resolution, arrays allow for multiple conflicting values which should be impossible. For example, if a user passes { theme: "dark" }
and there are 2 modifiers named "theme" and 2 modifier values named "dark", this creates ambiguity that should be an error rather than defaulting to "take the first one." While arrays make sense for defining order of application, if we want key-value mapping for applying modifiers, flat objects should be required to ensure unique IDs by design.
-
- The specification needs to clarify what constitutes valid values in the values
array for modifiers. Key questions include: Are only relative pathnames allowed? Can referenced files have sets/modifiers of their own (enabling resolver chaining)? Should there be support for referencing sets by name (e.g., "foundation") rather than repeating file paths? The concept of chaining resolvers could use different type values to identify other resolvers. Additionally, values could support local references starting with "#" or string references to set names, making it easier to reference entire sets without listing all token file paths.
-
Example of an "include" 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.
--{ - "modifiers": [ - { - "name": "features", - "type": "include", - "values": [ - { - "name": "experimental-feature-x", - "values": ["features/feature-x.json"] - } - ] - } - ] -} --
Aliasing allows for dynamic namespacing or renaming of token paths during resolution. This is particularly useful when integrating external token sets or avoiding naming conflicts.
-Namespace vs Alias Terminology: Should the alias
property be renamed to namespace
to avoid confusion with token aliases (references to other tokens)?
Redundancy with Modifier Names: The meta.alias
property may be redundant since modifiers already have a name
property that could serve the same namespacing purpose.
Auto-namespacing Concerns: The automatic application of namespacing for modifiers introduces significant complexity and potential issues:
-Alternative Approach: Consider removing auto-namespacing for modifiers and using shared merging logic between sets and modifiers for better compatibility and simplicity.
-Example:
-Given a token set size.json:
--{ - "sm": { - "value": "1px", - "type": "dimension" - }, - "lg": { - "value": "10px", - "type": "dimension" - } -} --
By applying an alias in the modifier's meta.alias, we can namespace these tokens:
--{ - "modifiers": [ - { - "name": "size", - "type": "include", - "values": [ - { - "name": "default", - "values": ["size.json"] - } - ], - "meta": { - "alias": "spacing" - } - } - ] -} --
Resulting in tokens accessible via spacing.sm and spacing.lg.
-
- If the meta.alias
behavior described above is normative/required behavior, it should not be part of the generic meta
property but should be defined as part of the formal schema. Alternatively, if this is just an example of tooling-specific behavior, it should be clearly called out as such and not presented as part of the core specification.
-
Source: https://resolver-spec.netlify.app/reference/schema/
--{ - "$id": "https://schemas.tokens.studio/prototype/resolver.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "Resolver Specification", - "$defs": { - "tokenSet": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "values": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": ["values"] - }, - "modifier": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "values": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "values": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": ["name", "values"] - } - }, - "meta": { - "type": "object", - "additionalProperties": true - } - }, - "required": ["name", "values"] - } - }, - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "sets": { - "type": "array", - "items": { - "$ref": "#/$defs/tokenSet" - } - }, - "modifiers": { - "type": "array", - "items": { - "$ref": "#/$defs/modifier" - } - } - }, - "required": ["sets", "modifiers"] -} --
The resolution process involves the following steps:
-- The term "inputs" is used throughout the specification but never formally defined. Inputs appear to represent the permutation or combination of modifiers being selected for resolution - essentially the question "What would my output look like given some combination of modifiers being enabled or chosen?" This fundamental concept should be clearly defined, potentially as "data" or with a more specific term that clarifies its role in the resolution process. -
-
- The merging process needs detailed specification to handle DTCG token data correctly. Simply performing a deep merge on raw JSON could yield different results than fully interpreting the DTCG data first and then merging. This is particularly important for inheritable format properties like $type
. For example, when loading two JSON files with a top-level "color" key where one has $type: "color"
and another has $type: "gradient"
, these represent incompatible schemas that cannot be merged. The specification must outline what constitutes "mergeable" vs "unmergeable" token sets and provide clear rules for handling such conflicts.
-
- The specification needs clear step-by-step instructions for how deep merging should occur, including detailed guidance on handling edge cases or when to error appropriately. This should include specific algorithms and decision trees for implementers to follow consistently. -
-- The specification should clarify the resolution order when multiple modifiers are applied simultaneously. For instance, if both "theme" and "brand" modifiers are used and both attempt to override the same token, which modifier should take precedence? Clear precedence rules are needed to ensure consistent and predictable resolution behavior across implementations. -
-- The specification needs to clarify the different types of "aliasing" and their behavior during resolution. There are two distinct concepts being referred to as "aliasing": (1) Namespacing aliasing - where tokens are renamed/namespaced (e.g., "red.500" becomes "colors.red.500"), and (2) Token reference aliasing - where one token's value references another token's value. The specification should clearly distinguish between these concepts and address how namespacing aliases behave when tokens are overridden by later sets - specifically, if an alias points to a token that gets overridden, does the alias resolve to the new/overridden value? -
-Alias resolution is performed on the fully merged set of tokens, after all base sets and modifiers have been applied. This allows for aliases to reference tokens from any loaded file.
-We need to decide if the resolver spec also follows the $name, $values, etc.
--{ - "name": "Example Resolver", - "sets": [ - { - "name": "foundation", - "values": ["foundation.json"] - }, - { - "values": ["components/button.json"] - } - ], - "modifiers": [ - { - "name": "theme", - "type": "enumerated", - "values": [ - { - "name": "light", - "values": ["themes/light.json"] - }, - { - "name": "dark", - "values": ["themes/dark.json"] - } - ], - "meta": { - "default": "light", - "alias": "theme" - } - } - ] -} --
- The alias
property should be renamed to namespace
to make its purpose clearer. Additionally, it should be moved out of the generic meta
property and included in the formal schema definition. Currently, any arbitrary value could exist associated with alias
since it's within the unstructured meta
object. Anything that is not 100% discardable should not live in meta
.
-
-{ - "theme": "dark" -} --
-{ - "color": { - "brand": { - "primary": { - "$value": "#FF0000", - "$type": "color" - } - } - } -} --
-{ - "button": { - "background": { - "$value": "{theme.accent}", - "$type": "color" - }, - "padding": { - "$value": "8px", - "$type": "dimension" - } - } -} --
-{ - "accent": { - "$value": "#00FF00", - "$type": "color" - } -} --
Merge (flatten) tokens, resulting in:
--{ - "color": { - "brand": { - "primary": { - "$value": "#FF0000", - "$type": "color" - } - } - }, - "button": { - "background": { - "$value": "{theme.accent}", - "$type": "color" - }, - "padding": { - "$value": "8px", - "$type": "dimension" - } - } -} --
Apply aliasing as per meta.alias ("theme"), resulting in:
--{ - "theme": { - "accent": { - "$value": "#00FF00", - "$type": "color" - } - } -} --
Alias resolution is performed on the fully merged set of tokens, after all base sets and modifiers have been applied. This allows for aliases to reference tokens from any loaded file.
--{ - "color": { - "brand": { - "primary": { - "$value": "#FF0000", - "$type": "color" - } - } - }, - "button": { - "background": { - "$value": "#00FF00", - "$type": "color" - }, - "padding": { - "$value": "8px", - "$type": "dimension" - } - } -} --
The Resolver Specification can be integrated with tools like Style Dictionary to automate the generation of platform-specific design tokens.
-Example:
-In large design systems, you might have multiple brands, each with light and dark themes, and accessibility modes (e.g., high contrast).
-Implementation:
-Modifiers are considered orthogonal when they can be changed independently without affecting each other's resolution logic.
-Example of Orthogonal Modifiers:
-You can mix any theme with any brand, resulting in all possible combinations.
-Example of Non-Orthogonal Modifiers:
-In such cases, the resolver must handle dependencies between modifiers, potentially by validating acceptable combinations or structuring modifiers to reflect the dependencies.
-- This requires input from the resolver author to explicitly declare whether a modifier is purely orthogonal or not. This declaration should be upfront in the specification, otherwise lazy resolution cannot be supported without the resolver having to check the actual tokens in scope. + We thank the members of the Design Tokens Community Group for their + contributions and feedback, including:
-The Resolver Specification can be extended to handle more complex scenarios:
-Tools implementing the Resolver Specification MUST:
-We thank the members of the Design Tokens Community Group for their contributions and feedback, including:
This section is non-normative and intended for early reviewers.
-Aliasing allows for dynamic namespacing or renaming of token paths during resolution. This is particularly useful when integrating external token sets or avoiding naming conflicts.
-Example:
-Given a token set size.json:
--{ - "sm": { - "value": "1px", - "type": "dimension" - }, - "lg": { - "value": "10px", - "type": "dimension" - } -} --
By applying an alias in the modifier's meta.alias, we can namespace these tokens:
--{ - "modifiers": [ - { - "name": "size", - "type": "include", - "values": [ - { - "name": "default", - "values": ["size.json"] - } - ], - "meta": { - "alias": "spacing" - } - } - ] -} --
Resulting in tokens accessible via spacing.sm and spacing.lg.
-The GitHub Primer design system uses multiple dimensions, including themes and visual modes (e.g., colorblind modes). The Resolver Specification can represent these dimensions as modifiers, allowing for efficient resolution and management of tokens.
-- Question: Would it be worth to highlight some public design systems and how they would use the resolver spec for more relatable use cases? -
-- While public design system examples could be valuable, generic use cases showing common dimensional patterns might be more important as foundational examples: single dimension (1 brand), two dimensions (1 brand + 2 themes), three dimensions (2 brands + 2 themes each), etc. These generic patterns would be the "meat and potatoes" compared to specific design system examples being the "cherry on the cake." -
-In scenarios where modifiers are not orthogonal, the resolver may need to enforce acceptable combinations and handle dependencies between modifiers. This can be achieved by:
-Scenario: A company has multiple brands—Brand A and Brand B. Each brand has its own color palette and typography. Components like buttons and headers need to adapt based on the selected brand.
-- These examples could be moved to the Resolver Spec netlify app for better presentation. If it uses Astro, interactive code tabs could be added to make the examples more engaging and easier to understand through hands-on exploration. -
--{ - "name": "Brand Theming Resolver", - "sets": [ - { - "name": "base", - "values": ["tokens/base.json"] - }, - { - "values": ["tokens/components.json"] - } - ], - "modifiers": [ - { - "name": "brand", - "type": "enumerated", - "values": [ - { - "name": "brandA", - "values": ["tokens/brands/brandA.json"] - }, - { - "name": "brandB", - "values": ["tokens/brands/brandB.json"] - } - ], - "meta": { - "default": "brandA", - "alias": "brand" - } - } - ] -} --
-{ - "color": { - "text": { - "primary": { - "value": "#000000", - "type": "color" - } - } - }, - "font": { - "family": { - "default": { - "value": "Arial, sans-serif", - "type": "font" - } - } - } -} --
-{ - "button": { - "background": { - "value": "{brand.color.primary}", - "type": "color" - }, - "fontFamily": { - "value": "{brand.font.family}", - "type": "font" - } - }, - "header": { - "color": { - "value": "{brand.color.secondary}", - "type": "color" - } - } -} --
-{ - "color": { - "primary": { - "value": "#FF5733", - "type": "color" - }, - "secondary": { - "value": "#C70039", - "type": "color" - } - }, - "font": { - "family": { - "value": "'Helvetica Neue', sans-serif", - "type": "font" - } - } -} --
-{ - "color": { - "primary": { - "value": "#1F618D", - "type": "color" - }, - "secondary": { - "value": "#2874A6", - "type": "color" - } - }, - "font": { - "family": { - "value": "'Times New Roman', serif", - "type": "font" - } - } -} --
-{ - "brand": "brandB" -} --
Load tokens/base.json and tokens/components.json.
-Merge tokens:
--{ - "color": { - "text": { - "primary": { - "value": "#000000", - "type": "color" - } - } - }, - "font": { - "family": { - "default": { - "value": "Arial, sans-serif", - "type": "font" - } - } - }, - "button": { - "background": { - "value": "{brand.color.primary}", - "type": "color" - }, - "fontFamily": { - "value": "{brand.font.family}", - "type": "font" - } - }, - "header": { - "color": { - "value": "{brand.color.secondary}", - "type": "color" - } - } -} --
Apply the "brand" modifier with value "brandB".
-Load tokens/brands/brandB.json.
-Apply aliasing as per meta.alias ("brand"), resulting in:
--{ - "brand": { - "color": { - "primary": { - "value": "#1F618D", - "type": "color" - }, - "secondary": { - "value": "#2874A6", - "type": "color" - } - }, - "font": { - "family": { - "value": "'Times New Roman', serif", - "type": "font" - } - } - } -} --
No conflicts in this example.
--{ - "color": { - "text": { - "primary": { - "value": "#000000", - "type": "color" - } - } - }, - "font": { - "family": { - "default": { - "value": "Arial, sans-serif", - "type": "font" - } - } - }, - "button": { - "background": { - "value": "#1F618D", - "type": "color" - }, - "fontFamily": { - "value": "'Times New Roman', serif", - "type": "font" - } - }, - "header": { - "color": { - "value": "#2874A6", - "type": "color" - } - } -} --
Scenario: A design system includes buttons that change appearance based on their state—default, hover, active, and disabled. We want to manage these state-specific styles using modifiers.
--{ - "name": "Component States Resolver", - "sets": [ - { - "values": ["tokens/components/button.json"] - } - ], - "modifiers": [ - { - "name": "state", - "type": "enumerated", - "values": [ - { - "name": "default", - "values": ["tokens/states/default.json"] - }, - { - "name": "hover", - "values": ["tokens/states/hover.json"] - }, - { - "name": "active", - "values": ["tokens/states/active.json"] - }, - { - "name": "disabled", - "values": ["tokens/states/disabled.json"] - } - ], - "meta": { - "default": "default" - } - } - ] -} --
-{ - "button": { - "background": { - "value": "{state.background}", - "type": "color" - }, - "textColor": { - "value": "{state.textColor}", - "type": "color" - }, - "borderColor": { - "value": "{state.borderColor}", - "type": "color" - } - } -} --
-{ - "background": { - "value": "#FFFFFF", - "type": "color" - }, - "textColor": { - "value": "#000000", - "type": "color" - }, - "borderColor": { - "value": "#CCCCCC", - "type": "color" - } -} --
-{ - "background": { - "value": "#F0F0F0", - "type": "color" - }, - "textColor": { - "value": "#000000", - "type": "color" - }, - "borderColor": { - "value": "#BBBBBB", - "type": "color" - } -} --
-{ - "background": { - "value": "#E0E0E0", - "type": "color" - }, - "textColor": { - "value": "#000000", - "type": "color" - }, - "borderColor": { - "value": "#AAAAAA", - "type": "color" - } -} --
-{ - "background": { - "value": "#F9F9F9", - "type": "color" - }, - "textColor": { - "value": "#777777", - "type": "color" - }, - "borderColor": { - "value": "#DDDDDD", - "type": "color" - } -} --
-{ - "state": "hover" -} --
Load tokens/components/button.json.
-Tokens:
--{ - "button": { - "background": { - "value": "{state.background}", - "type": "color" - }, - "textColor": { - "value": "{state.textColor}", - "type": "color" - }, - "borderColor": { - "value": "{state.borderColor}", - "type": "color" - } - } -} --
Apply the "state" modifier with value "hover".
-Load tokens/states/hover.json.
-Tokens under the "state" namespace:
--{ - "state": { - "background": { - "value": "#F0F0F0", - "type": "color" - }, - "textColor": { - "value": "#000000", - "type": "color" - }, - "borderColor": { - "value": "#BBBBBB", - "type": "color" - } - } -} --
No conflicts in this example.
--{ - "button": { - "background": { - "value": "#F0F0F0", - "type": "color" - }, - "textColor": { - "value": "#000000", - "type": "color" - }, - "borderColor": { - "value": "#BBBBBB", - "type": "color" - } - } -} --
Scenario: A design system needs to support responsive design by adjusting spacing and typography based on screen sizes—mobile, tablet, and desktop. We want to manage these variations using modifiers.
--{ - "name": "Responsive Design Resolver", - "sets": [ - { - "name": "core", - "values": ["tokens/core.json"] - } - ], - "modifiers": [ - { - "name": "screenSize", - "type": "enumerated", - "values": [ - { - "name": "mobile", - "values": ["tokens/screens/mobile.json"] - }, - { - "name": "tablet", - "values": ["tokens/screens/tablet.json"] - }, - { - "name": "desktop", - "values": ["tokens/screens/desktop.json"] - } - ], - "meta": { - "default": "mobile", - "alias": "screen" - } - } - ] -} --
-{ - "spacing": { - "small": { - "value": "{screen.spacing.small}", - "type": "dimension" - }, - "medium": { - "value": "{screen.spacing.medium}", - "type": "dimension" - }, - "large": { - "value": "{screen.spacing.large}", - "type": "dimension" - } - }, - "typography": { - "fontSize": { - "value": "{screen.typography.fontSize}", - "type": "dimension" - } - } -} --
-{ - "spacing": { - "small": { - "value": "4px", - "type": "dimension" - }, - "medium": { - "value": "8px", - "type": "dimension" - }, - "large": { - "value": "12px", - "type": "dimension" - } - }, - "typography": { - "fontSize": { - "value": "14px", - "type": "dimension" - } - } -} --
-{ - "spacing": { - "small": { - "value": "6px", - "type": "dimension" - }, - "medium": { - "value": "12px", - "type": "dimension" - }, - "large": { - "value": "18px", - "type": "dimension" - } - }, - "typography": { - "fontSize": { - "value": "16px", - "type": "dimension" - } - } -} --
-{ - "spacing": { - "small": { - "value": "8px", - "type": "dimension" - }, - "medium": { - "value": "16px", - "type": "dimension" - }, - "large": { - "value": "24px", - "type": "dimension" - } - }, - "typography": { - "fontSize": { - "value": "18px", - "type": "dimension" - } - } -} --
-{ - "screenSize": "desktop" -} --
Load tokens/core.json.
-Tokens:
--{ - "spacing": { - "small": { - "value": "{screen.spacing.small}", - "type": "dimension" - }, - "medium": { - "value": "{screen.spacing.medium}", - "type": "dimension" - }, - "large": { - "value": "{screen.spacing.large}", - "type": "dimension" - } - }, - "typography": { - "fontSize": { - "value": "{screen.typography.fontSize}", - "type": "dimension" - } - } -} --
Apply the "screenSize" modifier with value "desktop".
-Load tokens/screens/desktop.json.
-Apply aliasing as per meta.alias ("screen"), resulting in:
--{ - "screen": { - "spacing": { - "small": { - "value": "8px", - "type": "dimension" - }, - "medium": { - "value": "16px", - "type": "dimension" - }, - "large": { - "value": "24px", - "type": "dimension" - } - }, - "typography": { - "fontSize": { - "value": "18px", - "type": "dimension" - } - } - } -} --
No conflicts in this example.
--{ - "spacing": { - "small": { - "value": "8px", - "type": "dimension" - }, - "medium": { - "value": "16px", - "type": "dimension" - }, - "large": { - "value": "24px", - "type": "dimension" - } - }, - "typography": { - "fontSize": { - "value": "18px", - "type": "dimension" - } - } -} --