Skip to content

Adding technical report: Resolver #289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
"license": "SEE LICENSE.md",
"packageManager": "[email protected]",
"scripts": {
"build": "pnpm --filter @dtcg/www run build",
"build": "pnpm --filter @dtcg/tr run build && pnpm --filter @dtcg/www run build",
"dev": "pnpm run build && pnpm --parallel --recursive --if-present run dev",
"lint": "pnpm --recursive --parallel --stream --if-present run lint",
"format": "pnpm --recursive --parallel --stream --if-present run format",
"prepare": "husky",
"install-browsers": "puppeteer browsers install chrome@137"
"install-browsers": "puppeteer browsers install chrome"
},
"lint-staged": {
"*.{md,yml,json,html}": "prettier --write"
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion technical-reports/format/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
{ name: 'Matthew Ström', url: 'https://matthewstrom.com' },
{ name: 'Mike Kamminga', url: 'https://x.com/mikekamminga' },
],

github: {
repoURL: 'https://github.com/design-tokens/community-group',
branch: 'main',
Expand Down
2 changes: 1 addition & 1 deletion technical-reports/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ <h1>Modules</h1>
<a href="./format/">Format</a>
</li>
<li><a href="./color/">Color</a></li>
<li>Animations (coming soon)</li>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I have no idea what happens to animations but in the future I’d love to resume this exploration (I’m no Val Head but I’m interested and have some knowledge here)

<li><a href="./resolver/">Resolver</a></li>
</ul>
</section>
<section
Expand Down
19 changes: 11 additions & 8 deletions technical-reports/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,27 @@
"directory": "technical-reports"
},
"scripts": {
"build": "pnpm run build:index && pnpm run build:format && pnpm run build:color",
"build:index": "pnpm run mkdirs && respec index.html ../www/TR/drafts/index.html --port 3001 --localhost --disable-sandbox",
"build:format": "pnpm run mkdirs && respec format/index.html ../www/TR/drafts/format/index.html --port 3002 --localhost --disable-sandbox",
"build:color": "pnpm run mkdirs && respec color/index.html ../www/TR/drafts/color/index.html --port 3003 --localhost --disable-sandbox",
"mkdirs": "mkdir -p ../www/TR/drafts && mkdir -p ../www/TR/drafts/format && mkdir -p ../www/TR/drafts/color",
"build": "pnpm run mkdirs && pnpm run build:index && pnpm run build:format && pnpm run build:color && pnpm run build:resolver",
"build:index": "respec index.html ../www/TR/drafts/index.html --port 3001 --localhost --disable-sandbox",
"build:format": "respec format/index.html ../www/TR/drafts/format/index.html --port 3002 --localhost --disable-sandbox",
"build:color": "respec color/index.html ../www/TR/drafts/color/index.html --port 3003 --localhost --disable-sandbox",
"build:resolver": "respec resolver/index.html ../www/TR/drafts/resolver/index.html --port 3004 --localhost --disable-sandbox",
"mkdirs": "mkdir -p ../www/TR/drafts && mkdir -p ../www/TR/drafts/format && mkdir -p ../www/TR/drafts/color && mkdir -p ../www/TR/drafts/resolver",
"dev": "pnpm --parallel --filter @dtcg/tr run \"/^dev:.*/\"",
"dev:index": "pnpm run mkdirs && chokidar \"index.html\" -c \"pnpm run build:index\" -d 5000",
"dev:format": "pnpm run mkdirs && chokidar \"format/**/*\" -c \"pnpm run build:format\" -d 5000",
"dev:color": "pnpm run mkdirs && chokidar \"color/**/*\" -c \"pnpm run build:color\" -d 5000",
"dev:resolver": "pnpm run mkdirs && chokidar \"resolver/**/*\" -c \"pnpm run build:resolver\" -d 5000",
"lint": "prettier . --check",
"format": "prettier . --format --write",
"validate": "pnpm run validate:index && pnpm run validate:format",
"validate": "pnpm run validate:index && pnpm run validate:format && pnpm run validate:resolver",
"validate:index": "respec --localhost index.html --haltonerror --haltonwarn",
"validate:format": "respec --localhost format/index.html --haltonerror --haltonwarn"
"validate:format": "respec --localhost format/index.html --haltonerror --haltonwarn",
"validate:resolver": "respec --localhost resolver/index.html --haltonerror --haltonwarn"
},
"devDependencies": {
"chokidar": "^4.0.3",
"chokidar-cli": "^3.0.0",
"respec": "^35.5.0"
"respec": "^35.5.1"
}
}
50 changes: 50 additions & 0 deletions technical-reports/resolver/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
7 changes: 7 additions & 0 deletions technical-reports/resolver/conformance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Tools implementing the Resolver Specification MUST:

- **Support the Resolution Process**: Implement the [resolution logic](#resolution-logic) as defined, including input validation, base set flattening, modifier application, aliasing, and conflict resolution.
- **Validate Inputs**: Ensure that provided modifier inputs match the defined modifiers and acceptable values.
- **Resolve Aliases Correctly**: Handle [token references](../format/#aliases-references) accurately, including recursive references and detection of circular dependencies.
- **Preserve Token Properties**: Maintain additional token properties (e.g., [$extensions](#extensions)) throughout the resolution process.
- **Handle Errors Gracefully**: Provide meaningful error messages for issues like invalid inputs or circular references.
116 changes: 116 additions & 0 deletions technical-reports/resolver/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Design Tokens Resolver Module</title>
<script
src="https://www.w3.org/Tools/respec/respec-w3c"
class="remove"
></script>
<script class="remove">
var respecConfig = {
specStatus: 'CG-DRAFT',
group: 'design-tokens',
// In PR preview apps, shows an annoying red box to the document,
// warning unsuspecting readers that they should not cite or
// reference this version of the specification.
isPreview: document.location.host.includes('preview'),
editors: [
{ name: "Andrew L'Homme", email: '[email protected]' },
{ name: 'Drew Powers' },
{ name: 'Esther Cheran', email: '[email protected]' },
{ name: 'James Nash' },
{ name: 'Joren Broekema', email: '[email protected]' },
{ name: 'Louis Chenais' },
{ name: 'Mike Kamminga', email: '[email protected]' },
],
shortName: 'design-tokens-resolvers',
cg: 'Design Tokens W3C Community Group',
cgURI: 'https://github.com/design-tokens/community-group',
github: {
repoURL: 'https://github.com/design-tokens/community-group',
branch: 'main',
},
tocIntroductory: true,
logos: [
{
src: '/_includes/assets/images/logo_128_2x.png',
url: 'https://www.designtokens.org',
alt: 'Design Tokens Community Group',
width: 128,
height: 128,
id: 'dtcg-logo',
},
],
};
</script>
</head>
<body>
<section id="abstract">
<p>
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.
</p>
</section>

<section id="sotd">
<p>
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.
</p>
<p>This document has been published to facilitate Wide Review.</p>
<p>
This document was produced by the Design Tokens W3C Community Group, and
contributions to this draft are governed by
<a href="https://www.w3.org/community/about/process/cla"
>Community Contributor License Agreement (CLA)</a
>, as specified by the
<a href="https://www.w3.org/community/about/process/#cgroups"
>W3C Community Group Process</a
>.
</p>
</section>

<section
class="informative"
data-include="./introduction.md"
data-include-format="markdown"
></section>

<section
data-include="./terminology.md"
data-include-format="markdown"
></section>

<section
data-include="./syntax.md"
data-include-format="markdown"
></section>

<section
data-include="./resolution-logic.md"
data-include-format="markdown"
></section>

<section
id="conformance"
data-include="./conformance.md"
data-include-format="markdown"
></section>

<section id="acknowledgements" class="appendix informative">
<h2>Acknowledgments</h2>
<p>
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.
</p>
</section>
</body>
</html>
105 changes: 105 additions & 0 deletions technical-reports/resolver/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Introduction

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 values=]while producing the fewest minimal end number. We’ll compare a [naïve approach](#naive-approach) to the [resolver approach](#resolver-approach) outlined in this document.

## Naïve approach

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 𝑇<sub>𝛥</sub>:

<math display="block">
<mrow>
<mi>T</mi>
<mo>×</mo>
<mi>AV</mi>
<mo>=</mo>
<msub><mi>T</mi><mi>Δ</mi></msub>
</mrow>
</math>

<aside class="example" title="Naïve token increase">

Starting with 100 [color tokens](../format/#color):

- Adding a 2nd “dark mode” layer of alternate values results in 200 values, or 100 × 2.
- Adding a 2nd (“dark mode”), 3rd (“light mode - high contrast”), and 4th (“dark mode - high contrast”) layer results in 400 values, or 100 × 4.

</aside>

## Resolver approach

The resolver approach involves breaking apart all tokens 𝑇 into subsets 𝑡<sub>1</sub>, 𝑡<sub>2</sub>, … 𝑡<sub>𝑛</sub>, and applying alternate value layers separately to produce a subtotal. The subtotals are added together to produce a final 𝑇<sub>𝛥</sub> value. The key difference is avoiding flat multiplication across the entire superset by breaking into subsets. Mathematically this may be expressed like so:

<math display="block">
<mtable>
<mtr>
<mtd>
<msub><mi>𝑡</mi><mn>1</mn></msub>
<mo>×</mo>
<msub><mi>AV</mi><mn>1</mn></msub>
</mtd>
<mtd>
<mo>=</mo>
</mtd>
<mtd>
<msub><mi>T</mi><mn>Δ1</mn></msub>
</mtd>
</mtr>
<mtr>
<mtd>
<msub><mi>𝑡</mi><mn>2</mn></msub>
<mo>×</mo>
<msub><mi>AV</mi><mn>2</mn></msub>
</mtd>
<mtd>
<mo>=</mo>
</mtd>
<mtd>
<msub><mi>T</mi><mn>Δ2</mn></msub>
</mtd>
</mtr>
<mtr>
<mtd></mtd><mtd>...</mtd><mtd></mtd>
</mtr>
<mtr>
<mtd>
<msub><mi>𝑡</mi><mi>n</mi></msub>
<mo>×</mo>
<msub><mi>AV</mi><mi>n</mi></msub>
</mtd>
<mtd>
<mo>=</mo>
</mtd>
<mtd>
<msub>
<mi>T</mi>
<mi>Δn</mi>
</msub>
</mtd>
</mtr>
<mtr>
<mtd>
<msub><mi>T</mi><mn>Δ1</mn></msub>
<mo>+</mo>
<msub><mi>T</mi><mn>Δ2</mn></msub>
<mo>+</mo>
<mi>…</mi>
<msub><mi>T</mi><mi>Δn</mi></msub>
</mtd>
<mtd><mo>=</mo></mtd>
<mtd><msub><mi>T</mi><mi>Δ</mi></msub></mtd>
</mtr>
</mtable>
</math>

<aside class="example" title="Resolver approach">

1. Subset 𝑎, consisting of 100 color tokens, applies 4 alternate value layers (100 × 4).
2. Subset 𝑏, consisting of 50 dimension tokens, applies 4 alternate value layers (50 × 4).
3. Subset 𝑐, consisting of 50 typography tokens, applies 2 alternate value layers (50 × 2).
4. Adding all subtotals together (400 + 200 + 100) produces 700 final values.

700 is much fewer than the 2,000 tokens you’d get from the naïve method (100 × (4 + 4 + 2))!

</aside>

This illustrates the concept in abstract. See [syntax](#syntax) to see how it’s expressed in JSON.
Loading