diff --git a/technical-reports/format/aliases.md b/technical-reports/format/aliases.md index dddff0d4..eab54835 100644 --- a/technical-reports/format/aliases.md +++ b/technical-reports/format/aliases.md @@ -1,4 +1,4 @@ -# 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. @@ -6,35 +6,440 @@ Aliases are useful for: - Expressing design choices - Eliminating repetition of values in token files (DRYing up the code) +- Creating semantic relationships between tokens +- Maintaining consistency across related values -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. +## Reference Syntax -For example: +Design tokens support two distinct syntaxes for referencing content within token files: + +### Curly Brace Syntax (Token References) + +The curly brace syntax is specifically designed for referencing **complete token values** and always resolves to the `$value` property of the target token. + + + +In this example, `{colors.blue}` resolves to the entire color object `{"colorSpace": "srgb", "components": [0, 0.4, 0.8], "hex": "#0066cc"}`. + +**Important:** Curly brace references can ONLY target complete tokens (objects with `$value` properties), not individual properties within token values or arbitrary document locations. + +### JSON Pointer Syntax (Required Support) + +For advanced use cases requiring access to specific properties within token values or other parts of the document structure, tokens MUST support JSON Pointer notation as defined by [[rfc6901]], using the `$ref` property. Tools implementing this specification MUST support JSON Pointer syntax. + +In this example: + +- `"$ref": "#/colors/blue/$value"` is equivalent to `"{colors.blue}"` +- `"$ref": "#/colors/blue/$value/components/0"` accesses just the red component (0) of the blue color + +**Key Differences:** + +| Aspect | Curly Brace `{token}` | JSON Pointer `$ref` | +| ----------------- | ------------------------- | --------------------------- | +| **Targets** | Complete tokens only | Any document location | +| **Implicit path** | Always appends `/$value` | Explicit full path required | +| **Use case** | Token-to-token references | Property-level references | +| **Syntax** | `{group.token}` | `#/group/token/$value` | + +## Reference Resolution + +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. 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. + +### Resolution Algorithms + +**For Curly Brace References:** + +1. **Parse reference:** Extract token path from `{group.token}` +2. **Split path:** Convert to segments `["group", "token"]` +3. **Navigate to token:** Find the target token object +4. **Validate token:** Ensure target has `$value` property +5. **Return token value:** Extract and return the `$value` content +6. **Check for cycles:** Maintain stack of resolving references + +**For JSON Pointer References:** + +1. **Parse JSON Pointer:** Extract path segments from `#/path/to/target` +2. **Traverse document:** Navigate through each path segment +3. **Apply JSON Pointer rules:** + - Numeric segments in array contexts become array indices + - All segments in object contexts become property names + - Handle escaped characters (`~0`, `~1`) +4. **Validate target:** Ensure the final target exists and is accessible +5. **Return value:** Extract and return the resolved value + +### Chained References + +Aliases MAY reference other aliases. In this case, tools MUST follow each reference until they find a token with an explicit value. + + -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. +In this example, `{semantic.link}` resolves to the same color value as `{base.primary}` by following the chain: `semantic.link` → `semantic.brand` → `base.primary`. + +### Circular References + +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. + + + +Tools MUST detect and report this as an error affecting all tokens in the circular chain. + +## Property-Level References + +JSON Pointer syntax enables references to specific properties within composite tokens, not just entire token values. This enables fine-grained reuse of token components while maintaining semantic relationships. + +**Important:** Property-level references require JSON Pointer syntax (`$ref`) and cannot be expressed using curly brace syntax. + +### Color Component References + + + +In this example: + +- `semantic.primary` keeps the same red and green components as `base.blue` but uses a different blue component (0.7) +- `semantic.secondary` keeps the same red and green components as `base.blue` but uses a different blue component (0.5) +- Changes to the hue of `base.blue` will automatically propagate to both semantic colors + +### Dimension Component References + + + +In this example: + +- `layout.small` uses the same numeric value as `base.spacing` (16) but with a different unit (`rem`) +- `layout.large` uses the same unit as `base.spacing` (`px`) but with a different numeric value (32) + +### Typography Component References + + + +In this example: + +- Both headings inherit the `fontFamily` and `lineHeight` from `base.text` +- Each heading defines its own `fontSize` and `fontWeight` +- Changes to the base font family or line height automatically affect all headings + +## JSON Pointer Path Construction and Resolution + +JSON Pointer syntax provides direct access to any location within the design token document structure, following standard JSON Pointer rules as defined by [[rfc6901]]. + +### Path Construction Rules + +- **Root reference:** `#/` (refers to the document root) +- **Object properties:** `/` separates each level (e.g., `#/group/token/$value`) +- **Array indices:** Numeric indices for array elements (e.g., `#/color/$value/components/0`) +- **Special characters:** Must be escaped according to JSON Pointer rules: + - `~` becomes `~0` + - `/` becomes `~1` + +### Token Value Access Patterns + +Since design tokens store their values in `$value` properties, JSON Pointer paths to token values follow a predictable pattern: + +| JSON Pointer | Equivalent Curly Brace Reference | Description | +| ----------------------------------- | -------------------------------- | ------------------------ | +| `#/colors/blue/$value` | `{colors.blue}` | Complete token value | +| `#/colors/blue/$value/hex` | N/A | Hex property of color | +| `#/colors/blue/$value/components/0` | N/A | First component of color | +| `#/colors/blue/$type` | N/A | Token type metadata | + +### Resolution Algorithm for JSON Pointer + +1. **Parse JSON Pointer:** Extract path segments from `#/path/to/target` +2. **Traverse document:** Navigate through each path segment +3. **Apply JSON Pointer rules:** + - Numeric segments in array contexts become array indices + - All segments in object contexts become property names + - Handle escaped characters (`~0`, `~1`) +4. **Validate target:** Ensure the final target exists and is accessible +5. **Return value:** Extract and return the resolved value + +### Curly Brace Resolution Algorithm + +1. **Parse reference:** Extract token path from `{group.token}` +2. **Split path:** Convert to segments `["group", "token"]` +3. **Navigate to token:** Find the target token object +4. **Validate token:** Ensure target has `$value` property +5. **Return token value:** Extract and return the `$value` content +6. **Check for cycles:** Maintain stack of resolving references + +### Error Conditions + +Tools MUST report errors for: + +- **Invalid syntax:** Malformed curly braces or JSON Pointer syntax +- **Unresolvable paths:** Target path does not exist in document +- **Invalid token references:** Curly brace syntax targeting non-tokens +- **Circular references:** Reference chains that loop back to themselves +- **Type mismatches:** Referenced value incompatible with expected type + +### Path Examples + +| Token Path | JSON Pointer | Curly Brace Syntax | +| -------------------- | ------------------------ | ------------------------ | +| Root token "primary" | `#/primary` | `{primary}` | +| Nested token | `#/colors/blue` | `{colors.blue}` | +| Array element | `#/color/components/0` | not supported | +| Property with space | `#/brand colors/primary` | `{brand colors.primary}` | +| Property with slash | `#/my~1group/token` | `{my/group.token}` | + +## Implementation Guidance + +### For Tool Authors + +Tools implementing design token parsing MUST: + +1. **Support curly brace syntax** as the primary reference mechanism for token-to-token references +2. **Support JSON Pointer syntax** for document-level references and property access +3. **Validate reference targets** to ensure they point to valid tokens (for curly brace) or valid document locations (for JSON Pointer) +4. **Resolve references** according to the resolution algorithms defined in this specification ([Resolution Algorithms](#resolution-algorithms)) +5. **Detect circular references** before attempting resolution +6. **Preserve references** in memory/storage and resolve only when values are needed +7. **Propagate changes** from referenced tokens to all aliases + +### Disambiguation Examples + + + +**Clear resolution:** + +| Reference | Result | Notes | +| :----------------------- | :------------------------ | :--------------------------------------- | +| `#/ambiguous/data/0` | `10` | JSON Pointer array index | +| `{ambiguous.metadata.0}` | `"Info about first item"` | curly brace object property | +| `{ambiguous.data.0}` | Error | curly braces cannot access array indices | +| `{ambiguous.metadata.2}` | Error | property "2" doesn't exist | + +### Error Conditions + +Tools MUST report errors for: + +- **Invalid reference syntax:** Malformed curly braces or JSON Pointer syntax +- **Unresolvable references:** Target path does not exist +- **Circular references:** Reference chains that loop back to themselves +- **Type mismatches:** Referenced property type incompatible with token type +- **Invalid property paths:** Attempting to reference non-existent properties + +## Relationship to JSON Schema + +The reference syntax defined in this specification provides compatibility with JSON Schema patterns while serving the specific needs of design token authoring: + +- **JSON Pointer compatibility:** Direct support for RFC 6901 JSON Pointer notation enables integration with JSON Schema tooling +- **Design token semantics:** Curly brace references provide token-specific syntax optimized for common token-to-token relationships +- **Complementary approaches:** Both syntaxes serve different use cases within the design token ecosystem -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. +**Important:** While JSON Pointer references (`$ref`) in design tokens follow the same syntax as JSON Schema `$ref`, curly brace references (`{token}`) are design token-specific and provide different semantics (automatic `$value` resolution and token-only targeting) compared to standard JSON Schema references. -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. +--- -
- The format editors are currently researching JSON Pointer syntax to inform the exact syntax for aliases in tokens. https://datatracker.ietf.org/doc/html/rfc6901#section-5 -
+_This specification provides a flexible, standards-based approach to token references that balances author ergonomics with technical precision, enabling both simple token aliases and sophisticated property-level relationships._ diff --git a/technical-reports/format/composite-types.md b/technical-reports/format/composite-types.md index 2b21096e..46f43da5 100644 --- a/technical-reports/format/composite-types.md +++ b/technical-reports/format/composite-types.md @@ -2,14 +2,45 @@ The types defined in the previous chapters such as color and dimension all have singular values. For example, the value of a color token is _one_ color. However, there are other aspects of UI designs that are a combination of multiple values. For instance, a shadow style is a combination of a color, X & Y offsets, a blur radius and a spread radius. -Every shadow style has the exact same parts (color, X & Y offsets, etc.), but their respective values will differ. Furthermore, each part's value (which is also known as a "sub-value") is always of the same type. A shadow's color must always be a [color](#color) value, its X offset must always be a [dimension](#dimension) value, and so on. Shadow styles are therefore combinations of values _that follow a pre-defined structure_. In other words, shadow styles are themselves a type. Types like this are called **composite types**. +Every shadow style has the exact same parts (color, X & Y offsets, etc.), but their respective values will differ. Furthermore, each part's value (which is also known as a "sub-value") is always of the same type. A shadow's color must always be a [color](#color) value, its X offset must always be a [dimension](types#dimension) value, and so on. Shadow styles are therefore combinations of values _that follow a pre-defined structure_. In other words, shadow styles are themselves a type. Types like this are called **composite types**. Specifically, a composite type has the following characteristics: - Its value is an object or array, potentially containing nested objects or arrays, following a pre-defined structure where the properties of the (nested) object(s) or the elements of the (nested) arrays are sub-values. -- Sub-values may be explicit `color` values or references to other design tokens that have sub-value's type (e.g. `"{some.other.token}"`). +- Sub-values may be explicit values (e.g. `color` values) or references to other design tokens that have the sub-value's type (e.g. `"{some.other.token}"`). -A design token whose type happens to be a composite type is sometimes also called a composite (design) token. Besides their type, there is nothing special about composite tokens. They can have all the other additional properties like [`$description`](#description) or [`$extensions`](#extensions). They can also be referenced by other design tokens. +### Array aliasing in composite types + +When a composite type contains array properties, each element in the array may be either an explicit value or a reference to a token of the appropriate type. References in arrays resolve to single values and do not cause array expansion or flattening. This allows for flexible composition where some array elements are references while others are explicit values. + +Array aliasing follows these principles: + +1. **Single value resolution**: References in arrays always resolve to a single value of the appropriate type, never to arrays themselves. +2. **No flattening**: When referencing an array, the entire referenced array is treated as a single element in the referencing array. +3. **Type safety**: Each array element (explicit or referenced) must conform to the expected sub-value type for that composite type. +4. **Mixed composition**: Arrays may freely mix explicit values and references. + +For example, a shadow token with an array value can mix references to other shadow tokens with explicit shadow objects: + +```json +{ + "layered-shadow": { + "$type": "shadow", + "$value": [ + "{base.shadow}", + { + "color": "{brand.accent}", + "offsetX": { "value": 4, "unit": "px" }, + "offsetY": { "value": 4, "unit": "px" }, + "blur": { "value": 8, "unit": "px" }, + "spread": { "value": 0, "unit": "px" } + } + ] + } +} +``` + +A design token whose type happens to be a composite type is sometimes also called a composite (design) token. Besides their type, there is nothing special about composite tokens. They can have all the other additional properties like [`$description`](design-token#description) or [`$extensions`](design-token#extensions). They can also be referenced by other design tokens.