-
Notifications
You must be signed in to change notification settings - Fork 66
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
Proposal: JSON Schema $ref for aliases (breaking change) #259
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,125 @@ | ||
# Aliases / references | ||
# Aliases (References) | ||
|
||
Instead of having explicit values, tokens can reference the value of another token. To put it another way, a token can be an alias for another token. This spec considers the terms "alias" and "reference" to be synonyms and uses them interchangeably. | ||
|
||
Aliases are useful for: | ||
|
||
- Expressing design choices | ||
- Eliminating repetition of values in token files (DRYing up the code) | ||
- Eliminating repetition of values in token files (<abbr title="Don’t Repeat Yourself">DRY</abbr>ing up the code) | ||
|
||
For a design token to reference another, its value MUST be a string containing the period-separated (`.`) path to the token it's referencing enclosed in curly brackets. | ||
At any part of the schema, you MAY refer to another part of the schema with an object that has a `$ref` key and a valid [JSON pointer string](https://datatracker.ietf.org/doc/html/rfc6901). This syntax is borrowed from [JSON Schema (2020-12)](https://json-schema.org/draft/2020-12/json-schema-core#name-schema-references). | ||
|
||
For example: | ||
### Referencing Tokens | ||
|
||
You MAY reference values in the same file by starting with the fragment character (`#`) followed by the token path, separated by forward slashes: | ||
|
||
<aside class="example"> | ||
|
||
```json | ||
{ | ||
"color": { | ||
"blue": { | ||
"4": { "$value": "#218bff", "$type": "color" } | ||
}, | ||
"brand": { | ||
"$description": "Brand color", | ||
"$ref": "#/color/blue/4" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
`color.brand` aliases `color.blue.4`, and SHOULD always share its value (even at runtime). | ||
|
||
</aside> | ||
|
||
### Aliasing Non-tokens | ||
|
||
It’s not just tokens that may be referenced; any part of the document may be referenced. For example, groups: | ||
|
||
<aside class="example"> | ||
|
||
```json | ||
{ | ||
"color": { | ||
"gray": { | ||
"1": { "$value": "#101010", "$type": "color" }, | ||
"2": { "$value": "#202020", "$type": "color" }, | ||
"3": { "$value": "#404040", "$type": "color" }, | ||
"4": { "$value": "#808080", "$type": "color" } | ||
}, | ||
"grey": { "$ref": "#/color/gray" } | ||
} | ||
} | ||
``` | ||
|
||
In this example, `color.grey.1` would be an alias for `color.gray.1`, `color.grey.2` would alias `color.gray.2`, etc. | ||
|
||
</aside> | ||
|
||
Any group or property (`$type`, `$value`, `$description`, `$extension`, etc.) may all be referenced, so long as the final resolved value results in a valid schema. | ||
|
||
### Aliasing Tokens in Other Files | ||
|
||
Aliasing tokens from other files is possible by specifing a URL, either relative (`./relative/path.json`) or absolute (`https://example.com/api/v1/tokens.json`). | ||
|
||
To only use part of a JSON structure, you MAY use the fragment character (`#`) after the URL, followed by [the token path](#referencing-tokens). | ||
|
||
<aside class="example"> | ||
|
||
```json | ||
{ | ||
"group name": { | ||
"token name": { | ||
"$value": 1234, | ||
"$type": "number" | ||
"space": { | ||
"sm": { | ||
"$description": { "$ref": "./shared.json#/space/sm/$description" }, | ||
"$value": { "$ref": "./shared.json#/space/sm/$value" }, | ||
"$type": { "$ref": "./shared.json#/space/sm/$type" } | ||
} | ||
}, | ||
"alias name": { | ||
"$value": "{group name.token name}" | ||
"typography": { | ||
"$ref": "https://example.com/api/v1/tokens/typography.json" | ||
} | ||
} | ||
``` | ||
|
||
</aside> | ||
|
||
When a tool needs the actual value of a token it MUST resolve the reference - i.e. lookup the token being referenced and fetch its value. In the above example, the "alias name" token's value would resolve to 1234 because it references the token whose path is `{group name.token name}` which has the value 1234. | ||
If there is no `#` character, the entire file will be loaded. So this means files MAY contain partial or incomplete schemas. The same rules apply—it MUST resolve to a valid, complete schema in the end. | ||
|
||
Tools SHOULD preserve references and therefore only resolve them whenever the actual value needs to be retrieved. For instance, in a [=design tool=], changes to the value of a token being referenced by aliases SHOULD be reflected wherever those aliases are being used. | ||
## Additional Info | ||
|
||
Aliases MAY reference other aliases. In this case, tools MUST follow each reference until they find a token with an explicit value. Circular references are not allowed. If a design token file contains circular references, then the value of all tokens in that chain is unknown and an appropriate error or warning message SHOULD be displayed to the user. | ||
When a tool needs the actual value of a token it MUST resolve all aliases and references, i.e. lookup the token being referenced and fetch its value. | ||
|
||
Tooling MUST allow `$ref` to have sibling values that act as overrides. In case of a conflict, tooling MUST keep the sibling values to `$ref`, i.e. `$ref` is resolved first, then any sibling keys are applied. | ||
|
||
<p class="ednote" title="JSON Pointer syntax"> | ||
The format editors are currently researching JSON Pointer syntax to inform the exact syntax for aliases in tokens. <a href="https://datatracker.ietf.org/doc/html/rfc6901#section-5">https://datatracker.ietf.org/doc/html/rfc6901#section-5</a> | ||
<p class="ednote" title="JSON Schema 2020-12"> | ||
JSON Schema 2020-12 was chosen over 2019-09 because it allows sibling overrides alongside `$ref`. | ||
</p> | ||
|
||
Tools SHOULD preserve all references, and only resolve them whenever the actual value needs to be retrieved. | ||
|
||
<aside class="example"> | ||
|
||
For example, if a token named `color.text.primary` was an alias of `color.palette.black`, the following CSS SHOULD be generated: | ||
|
||
##### ✅ Recommended | ||
|
||
```css | ||
:root { | ||
--color-palette-black: #000000; | ||
--color-text-primary: var(--color-palette-black); | ||
} | ||
``` | ||
|
||
##### ❌ Not Recommended | ||
|
||
```css | ||
:root { | ||
--color-palette-black: #000000; | ||
--color-text-primary: #000000; | ||
} | ||
``` | ||
|
||
</aside> | ||
|
||
Aliases MAY reference other aliases. In this case, tools MUST follow each reference until they find a token with an explicit value. Circular references are not allowed. If a design token file contains circular references, then the value of all tokens in that chain is unknown and an appropriate error or warning message SHOULD be displayed to the user. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ These definitions are focused on the technical aspects of the specification, aim | |
|
||
## (Design) Token | ||
|
||
A (Design) Token is information associated with a human readable name, at minimum a name/value pair. | ||
A (Design) Token is information associated with a human readable name, at minimum a name/value pair. | ||
|
||
For example: | ||
|
||
|
@@ -104,14 +104,33 @@ Groups are arbitrary and tools SHOULD NOT use them to infer the type or purpose | |
|
||
A design token's value can be a reference to another token. The same value can have multiple names or _aliases_. | ||
|
||
The following Sass example illustrates this concept: | ||
The following CSS example illustrates this concept: | ||
|
||
```scss | ||
$color-palette-black: #000000; | ||
$color-text-primary: $color-palette-black; | ||
```css | ||
:root { | ||
--color-palette-black: #000000; | ||
--color-text-primary: var(--color-palette-black); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: I also updated this earlier section introducing the term “Alias,” and used CSS variables rather than Sass variables. I think it better preserves this idea we want to keep—of aliases carrying through to runtime better. Plus I think in 2024, CSS variables are more widely-used than Sass variables (completely guessing; I have no data to back this up). |
||
} | ||
``` | ||
|
||
The value of `--color-text-primary` is `#000000`, because `--color-text-primary` _references `--color-palette-black`_. We can also say `--color-text-primary` is an _alias_ for `--color-palette-black.` | ||
|
||
In JSON notation, we define an alias the same way as [JSON Schema references](https://json-schema.org/draft/2020-12/json-schema-core#name-schema-references): | ||
|
||
```json | ||
{ | ||
"color": { | ||
"palette": { | ||
"black": { "$value": "#000000", "$type": "color" } | ||
}, | ||
"text": { | ||
"primary": { "$ref": "#/color/palette/black" } | ||
} | ||
} | ||
} | ||
``` | ||
|
||
The value of `$color-text-primary` is `#000000`, because `$color-text-primary` _references `$color-palette-black`_. We can also say `$color-text-primary` is an _alias_ for `$color-palette-black.` | ||
To learn more about syntax and uses, see [the definition on Aliases](#aliases-references). | ||
|
||
## Composite (Design) Token | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: color syntax is waiting on #257, but can be updated here if necessary. It’s unrelated to this proposal.