@adobe/design-data-spec 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,7 +16,6 @@ Each **invalid** case lives under `invalid/<RULE_ID>/` with:
16
16
  | `invalid/SPEC-005` | SPEC-005 | Dimension `default` not in `modes`. |
17
17
  | `invalid/SPEC-006` | SPEC-006 | Ambiguous resolution / specificity tie (warning). |
18
18
  | `invalid/SPEC-008` | SPEC-008 | Non-default mode variants with no base/default variant. |
19
- | `invalid/SPEC-014` | SPEC-014 | `lastModified` semver precedes `introduced`. |
20
19
 
21
20
  Implementors SHOULD run these fixtures once the Rust validator exposes rule IDs ([#724](https://github.com/adobe/spectrum-design-data/issues/724), [#725](https://github.com/adobe/spectrum-design-data/issues/725)).
22
21
 
@@ -49,20 +48,20 @@ Each **diff** case lives under `diff/<name>/` with:
49
48
  * `new/` — new token dataset
50
49
  * `expected.json` — full `DiffReport` structure (six category arrays: renamed, deprecated, reverted, added, deleted, updated)
51
50
 
52
- | Folder | Intent |
53
- | ----------------------------------- | ------------------------------------------------------------------------------- |
54
- | `diff/identical-tokens` | Two identical datasets MUST produce an empty diff (all arrays empty). |
55
- | `diff/simple-add-delete` | One old-only token → deleted; one new-only token → added. |
56
- | `diff/rename-by-uuid` | Same UUID, different name objects → renamed (not add + delete). |
57
- | `diff/deprecated-new-token` | Unmatched new token with `deprecated: true` → deprecated (not added). |
58
- | `diff/deprecated-set-level` | All set entries `deprecated: true` normalizes to token-level deprecated. |
59
- | `diff/reverted-token` | Matched token that loses `deprecated` → reverted (not updated). |
60
- | `diff/matched-gaining-deprecated` | Matched token that gains `deprecated` → updated (not deprecated). |
61
- | `diff/property-value-update` | Matched token with changed `value` → updated with property change. |
62
- | `diff/property-nested-change` | Nested object change reported at leaf path (e.g. `sets.light.value`). |
63
- | `diff/uuid-backfill` | Old lacks UUID, new gains it with same name object → paired (not add + delete). |
64
- | `diff/cross-format` | Legacy old + cascade new, paired by UUID across formats. |
65
- | `diff/rename-with-property-changes` | Renamed token with additional value changes populates `property_changes`. |
51
+ | Folder | Intent |
52
+ | ------------------------------------- | -------------------------------------------------------------------------------- |
53
+ | `diff/identical-tokens` | Two identical datasets MUST produce an empty diff (all arrays empty). |
54
+ | `diff/simple-add-delete` | One old-only token → deleted; one new-only token → added. |
55
+ | `diff/rename-by-uuid` | Same UUID, different name objects → renamed (not add + delete). |
56
+ | `diff/deprecated-new-token` | Unmatched new token with `deprecated: true` → deprecated (not added). |
57
+ | `diff/deprecated-set-level` | All set entries `deprecated: true` normalizes to token-level deprecated. |
58
+ | `diff/reverted-token` | Matched token that loses `deprecated` → reverted (not updated). |
59
+ | `diff/matched-gaining-deprecated` | Matched token that gains `deprecated` → updated (not deprecated). |
60
+ | `diff/property-value-update` | Matched token with changed `value` → updated with property change. |
61
+ | `diff/property-nested-change` | Nested object change reported at leaf path (e.g. `sets.light.value`). |
62
+ | `diff/uuid-backfill` | Old lacks UUID, new gains it with same name object → paired (not add + delete). |
63
+ | `diff/cross-format` | Legacy old + cascade new, paired by UUID across formats. |
64
+ | `diff/rename-with-property-changes` | Renamed token with additional value changes populates `property_changes`. |
66
65
 
67
66
  The Rust SDK drives these fixtures in `sdk/core/src/lib.rs` (`diff_conformance` module, closes [#788](https://github.com/adobe/spectrum-design-data/issues/788)).
68
67
 
@@ -76,17 +75,17 @@ Each **query** case lives under `query/<name>/` with:
76
75
  * `query.txt` — plain-text filter expression
77
76
  * `expected.json` — sorted array of matched token UUIDs
78
77
 
79
- | Folder | Intent |
80
- | ------------------------- | ------------------------------------------------------- |
81
- | `query/single-field` | Basic `key=value` equality filter. |
82
- | `query/and-conditions` | `,` (AND) requires all conditions to match. |
83
- | `query/or-conditions` | `\|` (OR) matches if any alternative matches. |
84
- | `query/negation` | `!=` matches non-equal values and absent fields. |
85
- | `query/wildcard-suffix` | Glob `*` at end of value matches prefix. |
86
- | `query/wildcard-prefix` | Glob `*` at start of value matches suffix. |
87
- | `query/empty-matches-all` | Empty filter expression is a universal match. |
88
- | `query/no-matches` | Filter with no matching tokens returns empty result. |
89
- | `query/schema-key` | `$schema` key queries the top-level `$schema` field. |
90
- | `query/and-or-precedence` | AND binds tighter than OR: `a,b\|c` = `(a AND b) OR c`. |
78
+ | Folder | Intent |
79
+ | ----------------------------- | ------------------------------------------------------------------------- |
80
+ | `query/single-field` | Basic `key=value` equality filter. |
81
+ | `query/and-conditions` | `,` (AND) requires all conditions to match. |
82
+ | `query/or-conditions` | `\|` (OR) matches if any alternative matches. |
83
+ | `query/negation` | `!=` matches non-equal values and absent fields. |
84
+ | `query/wildcard-suffix` | Glob `*` at end of value matches prefix. |
85
+ | `query/wildcard-prefix` | Glob `*` at start of value matches suffix. |
86
+ | `query/empty-matches-all` | Empty filter expression is a universal match. |
87
+ | `query/no-matches` | Filter with no matching tokens returns empty result. |
88
+ | `query/schema-key` | `$schema` key queries the top-level `$schema` field. |
89
+ | `query/and-or-precedence` | AND binds tighter than OR: `a,b\|c` = `(a AND b) OR c`. |
91
90
 
92
91
  The Rust SDK drives these fixtures in `sdk/core/src/lib.rs` (`query_conformance` module, closes [#788](https://github.com/adobe/spectrum-design-data/issues/788)).
@@ -0,0 +1,10 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-016",
6
+ "severity": "error",
7
+ "message_pattern": "value does not validate against declared $valueType schema"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,8 @@
1
+ [
2
+ {
3
+ "name": { "property": "bad-typography" },
4
+ "$valueType": "value-types/typography.schema.json",
5
+ "value": "not-an-object",
6
+ "uuid": "aaaaaaaa-0016-4000-8000-000000000001"
7
+ }
8
+ ]
@@ -0,0 +1,10 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-017",
6
+ "severity": "warning",
7
+ "message_pattern": "string name instead of a name object"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,7 @@
1
+ [
2
+ {
3
+ "name": "focus-ring-color-key-focus",
4
+ "value": "#0265dc",
5
+ "uuid": "aaaaaaaa-0017-4000-8000-000000000002"
6
+ }
7
+ ]
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": { "property": "drop-shadow-default" },
3
+ "$valueType": "value-types/drop-shadow.schema.json",
4
+ "value": [
5
+ {
6
+ "x": "0px",
7
+ "y": "1px",
8
+ "blur": "4px",
9
+ "spread": "0px",
10
+ "color": "rgba(0, 0, 0, 0.16)"
11
+ }
12
+ ],
13
+ "uuid": "aaaaaaaa-0010-4000-8000-000000000002"
14
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": { "property": "typography-scale", "scale": "desktop" },
3
+ "$valueType": "value-types/typography-scale.schema.json",
4
+ "value": { "fontSize": "14px", "lineHeight": "18px" },
5
+ "uuid": "aaaaaaaa-0010-4000-8000-000000000003"
6
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": { "property": "component-m-regular" },
3
+ "$valueType": "value-types/typography.schema.json",
4
+ "value": {
5
+ "fontFamily": "{sans-serif-font-family}",
6
+ "fontSize": "{font-size-100}",
7
+ "fontWeight": "{regular-font-weight}",
8
+ "letterSpacing": "{letter-spacing}",
9
+ "lineHeight": "{line-height-font-size-100}"
10
+ },
11
+ "uuid": "aaaaaaaa-0010-4000-8000-000000000001"
12
+ }
@@ -0,0 +1,7 @@
1
+ [
2
+ {
3
+ "name": "focus-ring-color-key-focus",
4
+ "value": "#0265dc",
5
+ "uuid": "aaaaaaaa-0017-4000-8000-000000000001"
6
+ }
7
+ ]
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/field.schema.json",
3
+ "specVersion": "1.0.0-draft",
4
+ "name": "scaleIndex",
5
+ "description": "Numeric scale index appended at the end of the serialized name (e.g., 100, 200, 900). Used by foundational scale tokens for spacing, color palette, font sizes, border widths, corner radii, and drop-shadow values.",
6
+ "kind": "numeric",
7
+ "registry": null,
8
+ "validation": "none",
9
+ "serialization": {
10
+ "position": 16
11
+ },
12
+ "scope": null,
13
+ "required": false,
14
+ "valueType": "integer"
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/design-data-spec",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Design Data Specification — prose, JSON Schemas, rule catalog, and conformance fixtures for Spectrum design data",
5
5
  "type": "module",
6
6
  "repository": {
package/rules/rules.yaml CHANGED
@@ -120,10 +120,37 @@ rules:
120
120
  introduced_in: "1.0.0-draft"
121
121
 
122
122
  - id: SPEC-014
123
- name: last-modified-not-before-introduced
123
+ name: composite-inline-alias-exists
124
124
  severity: error
125
- category: completeness
126
- assert: If lastModified is present, its semver MUST NOT precede introduced.
127
- message: "Token {token} has lastModified {lastModified} earlier than introduced {introduced}"
128
- spec_ref: spec/token-format.md#lifecycle-and-metadata
125
+ category: reference-integrity
126
+ assert: Inline alias references ({token-name}) within composite values MUST resolve to an existing token in the dataset.
127
+ message: "Inline alias target not found within composite value: {path}"
128
+ spec_ref: spec/token-format.md#inline-alias-references
129
+ introduced_in: "1.0.0-draft"
130
+
131
+ - id: SPEC-015
132
+ name: composite-inline-alias-type-compatible
133
+ severity: error
134
+ category: type-safety
135
+ assert: Resolved inline alias target within a composite value MUST have a value type compatible with the sub-value's expected type.
136
+ message: "Inline alias {path} within composite resolves to incompatible type"
137
+ spec_ref: spec/token-format.md#inline-alias-references
138
+ introduced_in: "1.0.0-draft"
139
+
140
+ - id: SPEC-016
141
+ name: value-type-match
142
+ severity: error
143
+ category: type-safety
144
+ assert: When $valueType is present on a token with a literal value, the value MUST validate against the referenced value-type schema.
145
+ message: "Token {token} value does not validate against declared $valueType schema {schema}"
146
+ spec_ref: spec/token-format.md#value-type-declaration-valuetype
147
+ introduced_in: "1.0.0-draft"
148
+
149
+ - id: SPEC-017
150
+ name: string-name-tech-debt
151
+ severity: warning
152
+ category: tech-debt
153
+ assert: Token names SHOULD be structured name objects. A plain string name is permitted as a temporary escape hatch but MUST be treated as tech debt and tracked for remediation.
154
+ message: 'Token "{name}" uses a string name instead of a name object — treat as tech debt and plan remediation'
155
+ spec_ref: spec/token-format.md#string-name-escape-hatch
129
156
  introduced_in: "1.0.0-draft"
@@ -23,8 +23,8 @@
23
23
  },
24
24
  "kind": {
25
25
  "type": "string",
26
- "enum": ["semantic", "dimension"],
27
- "description": "Whether this field participates in cascade resolution. 'dimension' fields drive cascade specificity; 'semantic' fields describe identity only."
26
+ "enum": ["semantic", "dimension", "numeric"],
27
+ "description": "Whether this field participates in serialization ordering or cascade resolution. 'semantic' fields describe identity and appear in the standard serialization order; 'dimension' fields drive cascade specificity; 'numeric' fields hold integer indices appended at the end of the serialized name."
28
28
  },
29
29
  "registry": {
30
30
  "oneOf": [
@@ -99,7 +99,19 @@
99
99
  "const": "1.0.0-draft"
100
100
  },
101
101
  "name": {
102
- "$ref": "#/$defs/nameObject"
102
+ "oneOf": [
103
+ { "$ref": "#/$defs/nameObject" },
104
+ {
105
+ "type": "string",
106
+ "minLength": 1,
107
+ "description": "String escape hatch for tokens that cannot be expressed as a name object. Valid but triggers SPEC-017 (tech-debt warning). See token-format.md#string-name-escape-hatch."
108
+ }
109
+ ]
110
+ },
111
+ "$valueType": {
112
+ "type": "string",
113
+ "format": "uri-reference",
114
+ "description": "URI reference to a value-type schema under schemas/value-types/. When present, value MUST validate against the referenced schema (rule SPEC-016)."
103
115
  },
104
116
  "value": {
105
117
  "description": "Literal value; type narrowed by value-type schemas where applicable.",
@@ -131,10 +143,6 @@
131
143
  "introduced": {
132
144
  "type": "string"
133
145
  },
134
- "lastModified": {
135
- "type": "string",
136
- "description": "Spec version when token value or metadata last changed (e.g. \"2.1.0\"). MUST be >= introduced (see SPEC-014)."
137
- },
138
146
  "deprecated": {
139
147
  "type": "string",
140
148
  "description": "Spec version when token was deprecated (e.g. \"3.2.0\"). Truthy = deprecated."
@@ -178,7 +186,19 @@
178
186
  "const": "1.0.0-draft"
179
187
  },
180
188
  "name": {
181
- "$ref": "#/$defs/nameObject"
189
+ "oneOf": [
190
+ { "$ref": "#/$defs/nameObject" },
191
+ {
192
+ "type": "string",
193
+ "minLength": 1,
194
+ "description": "String escape hatch for tokens that cannot be expressed as a name object. Valid but triggers SPEC-017 (tech-debt warning). See token-format.md#string-name-escape-hatch."
195
+ }
196
+ ]
197
+ },
198
+ "$valueType": {
199
+ "type": "string",
200
+ "format": "uri-reference",
201
+ "description": "URI reference to a value-type schema under schemas/value-types/. When present, the alias resolution chain MUST terminate at a token whose value validates against this schema."
182
202
  },
183
203
  "$ref": {
184
204
  "type": "string",
@@ -192,10 +212,6 @@
192
212
  "introduced": {
193
213
  "type": "string"
194
214
  },
195
- "lastModified": {
196
- "type": "string",
197
- "description": "Spec version when token value or metadata last changed (e.g. \"2.1.0\"). MUST be >= introduced (see SPEC-014)."
198
- },
199
215
  "deprecated": {
200
216
  "type": "string",
201
217
  "description": "Spec version when token was deprecated (e.g. \"3.2.0\"). Truthy = deprecated."
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/value-types/drop-shadow.schema.json",
4
+ "title": "Drop shadow composite value",
5
+ "description": "Array of shadow layers. Each layer defines offset, blur, spread, and color.",
6
+ "type": "array",
7
+ "items": {
8
+ "type": "object",
9
+ "properties": {
10
+ "x": { "type": "string" },
11
+ "y": { "type": "string" },
12
+ "blur": { "type": "string" },
13
+ "spread": { "type": "string" },
14
+ "color": { "type": "string" }
15
+ },
16
+ "required": ["x", "y", "blur", "spread", "color"],
17
+ "additionalProperties": false
18
+ },
19
+ "minItems": 1
20
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/value-types/typography-scale.schema.json",
4
+ "title": "Typography scale composite value",
5
+ "description": "Pairs a font size with its corresponding line height at a single typographic tier.",
6
+ "type": "object",
7
+ "properties": {
8
+ "fontSize": { "type": "string" },
9
+ "lineHeight": { "type": "string" }
10
+ },
11
+ "required": ["fontSize", "lineHeight"],
12
+ "additionalProperties": false
13
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/value-types/typography.schema.json",
4
+ "title": "Typography composite value",
5
+ "description": "Object bundling font properties for a typographic style. Sub-values may be literal strings or inline alias references ({token-name}).",
6
+ "type": "object",
7
+ "properties": {
8
+ "fontFamily": { "type": "string" },
9
+ "fontSize": { "type": "string" },
10
+ "fontWeight": { "type": "string" },
11
+ "letterSpacing": { "type": "string" },
12
+ "lineHeight": { "type": "string" }
13
+ },
14
+ "required": ["fontFamily", "fontSize", "fontWeight", "lineHeight"],
15
+ "additionalProperties": false
16
+ }
package/spec/evolution.md CHANGED
@@ -12,13 +12,13 @@ Tokens progress through the following lifecycle stages:
12
12
  introduced → active → deprecated → planned removal → removed
13
13
  ```
14
14
 
15
- | Stage | Token state |
16
- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
17
- | **Introduced** | Token first appears in the dataset. `introduced` field records the version. |
18
- | **Active** | Token is current and recommended for use. No `deprecated` field. |
15
+ | Stage | Token state |
16
+ | ------------------- | ------------------------------------------------------------------------------- |
17
+ | **Introduced** | Token first appears in the dataset. `introduced` field records the version. |
18
+ | **Active** | Token is current and recommended for use. No `deprecated` field. |
19
19
  | **Deprecated** | Token is no longer recommended. `deprecated` records the version. Consumers receive warnings. `replaced_by` points to the successor token(s) when a direct replacement exists. |
20
- | **Planned removal** | `plannedRemoval` records the target version. The token remains in the dataset but consumers should complete migration. |
21
- | **Removed** | Token is deleted from the dataset. Consumers that still reference it will break. |
20
+ | **Planned removal** | `plannedRemoval` records the target version. The token remains in the dataset but consumers should complete migration. |
21
+ | **Removed** | Token is deleted from the dataset. Consumers that still reference it will break. |
22
22
 
23
23
  ### Lifecycle fields
24
24
 
@@ -28,10 +28,10 @@ See [Token format — Lifecycle and metadata](token-format.md#lifecycle-and-meta
28
28
 
29
29
  When a token carries `replaced_by`:
30
30
 
31
- * Each target UUID **MUST** resolve to an existing token in the dataset (rule `SPEC-010`).
32
- * The target token **SHOULD NOT** itself be deprecated. Chains of replacements (A replaced by B, B replaced by C) are discouraged; authors should point directly to the final replacement.
33
- * For a single UUID (1:1 replacement), consumers can mechanically rewrite references.
34
- * For an array of UUIDs (one-to-many split), the `deprecated_comment` **MUST** explain which replacement applies in which context.
31
+ - Each target UUID **MUST** resolve to an existing token in the dataset (rule `SPEC-010`).
32
+ - The target token **SHOULD NOT** itself be deprecated. Chains of replacements (A replaced by B, B replaced by C) are discouraged; authors should point directly to the final replacement.
33
+ - For a single UUID (1:1 replacement), consumers can mechanically rewrite references.
34
+ - For an array of UUIDs (one-to-many split), the `deprecated_comment` **MUST** explain which replacement applies in which context.
35
35
 
36
36
  ## Migration windows
37
37
 
@@ -49,27 +49,27 @@ The specification follows [Semantic Versioning](https://semver.org/) for its pub
49
49
 
50
50
  ### Minor changes (backward compatible)
51
51
 
52
- * New tokens added to the dataset
53
- * New optional fields added to token schema
54
- * New validation rules added to the rule catalog
55
- * Tokens deprecated (with `deprecated` field)
56
- * Rule severity relaxed (error → warning)
57
- * New enum values added to registries
52
+ - New tokens added to the dataset
53
+ - New optional fields added to token schema
54
+ - New validation rules added to the rule catalog
55
+ - Tokens deprecated (with `deprecated` field)
56
+ - Rule severity relaxed (error → warning)
57
+ - New enum values added to registries
58
58
 
59
59
  ### Major changes (breaking)
60
60
 
61
- * Tokens removed from the dataset
62
- * Required fields added to token schema
63
- * Existing fields removed or type-changed
64
- * Rule severity tightened (warning → error)
65
- * Enum values removed from registries
66
- * Constraint tightening (e.g. stricter value validation)
61
+ - Tokens removed from the dataset
62
+ - Required fields added to token schema
63
+ - Existing fields removed or type-changed
64
+ - Rule severity tightened (warning → error)
65
+ - Enum values removed from registries
66
+ - Constraint tightening (e.g. stricter value validation)
67
67
 
68
68
  ### Patch changes (clarifications)
69
69
 
70
- * Typo fixes in spec prose
71
- * Clarifications to normative text that do not change conformance behavior
72
- * Test fixture additions or corrections
70
+ - Typo fixes in spec prose
71
+ - Clarifications to normative text that do not change conformance behavior
72
+ - Test fixture additions or corrections
73
73
 
74
74
  ## Legacy format contract
75
75
 
@@ -79,14 +79,13 @@ The `@adobe/spectrum-tokens` package continues to publish tokens in the **legacy
79
79
 
80
80
  ### Lifecycle field mapping
81
81
 
82
- | Cascade format | Legacy format |
83
- | -------------------------------------- | ------------------------------------- |
84
- | `deprecated: "3.2.0"` (version string) | `deprecated: true` (boolean) |
85
- | `replaced_by: "<uuid>"` | `renamed: "<target-token-name>"` |
86
- | `introduced` | Not emitted |
87
- | `lastModified` | Not emitted |
88
- | `plannedRemoval` | Not emitted |
89
- | `deprecated_comment` | `deprecated_comment` (passed through) |
82
+ | Cascade format | Legacy format |
83
+ | --------------------------------------- | ------------------------------------ |
84
+ | `deprecated: "3.2.0"` (version string) | `deprecated: true` (boolean) |
85
+ | `replaced_by: "<uuid>"` | `renamed: "<target-token-name>"` |
86
+ | `introduced` | Not emitted |
87
+ | `plannedRemoval` | Not emitted |
88
+ | `deprecated_comment` | `deprecated_comment` (passed through)|
90
89
 
91
90
  ### Coexistence during migration
92
91
 
package/spec/manifest.md CHANGED
@@ -26,9 +26,7 @@ A manifest **MUST** conform to [`manifest.schema.json`](../schemas/manifest.sche
26
26
 
27
27
  ### `include` / `exclude`
28
28
 
29
- **NORMATIVE:** Entries **MUST** be non-empty strings.
30
-
31
- > **Note:** Query notation syntax is fully defined in [Query](query.md) and is normative for programmatic use. Its normative use in manifest `include`/`exclude` is deferred to a post-`1.0.0-draft` revision pending conformance fixtures; until then, implementations **MUST** treat manifest query values as opaque identifiers.
29
+ **NORMATIVE:** Entries **MUST** be non-empty strings. The **query language** is **not** normative in `1.0.0-draft`; treat values as opaque identifiers for tooling until a future spec version defines syntax.
32
30
 
33
31
  ### `overrides`
34
32
 
package/spec/taxonomy.md CHANGED
@@ -77,23 +77,7 @@ Additional categories for variant and state are inherited from the existing name
77
77
  | Variant | `variant` | Variant within a component (e.g. accent, negative, primary). |
78
78
  | State | `state` | Interactive or semantic state (e.g. hover, focus, disabled). |
79
79
 
80
- **NOTE:** The categories above are filtered for semantic and layout token taxonomies. Additional taxonomies for other token types (color, typography, motion) will be defined in future spec versions — see the open follow-up RFC discussion [#806](https://github.com/adobe/spectrum-design-data/discussions/806) (Q3, taxonomy scoping). The taxonomy is built to scale as new concepts and terms are identified.
81
-
82
- ### Structure vs. component — when does the line move?
83
-
84
- The `structure` and `component` fields both answer "What?" but apply at different scopes. A useful rule of thumb:
85
-
86
- * Use `component` when the token belongs to a specific component's surface (e.g. `button-background-color` — the background color of the Button component).
87
- * Use `structure` when the token belongs to a reusable visual pattern that recurs **across** components (e.g. `container-padding` — padding for any container-shaped surface, regardless of which component owns it).
88
-
89
- **Worked example — `card`:**
90
-
91
- * As a `structure`: when "card" describes a layout primitive used inside many components (e.g. `card-padding-medium` on a list item, a popover body, or a modal header), the token is structure-scoped.
92
- * As a `component`: when "card" describes the dedicated Card component's own surfaces (e.g. `card-background-color` on the Card root), the token is component-scoped.
93
-
94
- The same word can validly appear in both fields across the dataset; they are independent. Authors choose based on whether the token's *meaning* generalizes across many components (structure) or is specific to one component's identity (component).
95
-
96
- Source: Nate Baldwin, "Naming conventions & shared taxonomy" — Design Data & Platforms onsite, April 1, 2026.
80
+ **NOTE:** The categories above are filtered for semantic and layout token taxonomies. Additional categories do and will exist for other token types (e.g. color, typography). The taxonomy is built to scale as new concepts and terms are identified.
97
81
 
98
82
  ## Component anatomy vs. token objects
99
83
 
@@ -12,13 +12,33 @@ A **token** is a JSON object that satisfies the Layer 1 schema [`token.schema.js
12
12
 
13
13
  A token **MUST** contain:
14
14
 
15
- 1. **`name`** — a JSON object (the **name object**) as defined below.
15
+ 1. **`name`** — a JSON object (the **name object**) as defined below, or a non-empty plain string (escape hatch — see [String-name escape hatch](#string-name-escape-hatch)).
16
16
  2. Exactly one of:
17
17
  * **`value`** — a literal token value (type depends on value-type schema), or
18
18
  * **`$ref`** — a string reference to another token (alias).
19
19
 
20
20
  A token **MUST NOT** include both `value` and `$ref`.
21
21
 
22
+ ### String-name escape hatch
23
+
24
+ A token's `name` field **MAY** be a non-empty plain string instead of a name object when the token's identity cannot be expressed using the structured taxonomy fields.
25
+
26
+ ```json
27
+ {
28
+ "name": "focus-ring-color-key-focus",
29
+ "value": "#0265dc",
30
+ "uuid": "aaaaaaaa-0012-4000-8000-000000000001"
31
+ }
32
+ ```
33
+
34
+ **NORMATIVE:** String-named tokens are schema-valid. Validators **MUST** accept them without a structural error.
35
+
36
+ **NORMATIVE:** String-named tokens **MUST** trigger rule SPEC-017 (severity: `warning`, category: `tech-debt`). The warning surfaces the token as tracked debt requiring future remediation.
37
+
38
+ **NORMATIVE:** String-named tokens **MUST NOT** participate in name-object cascade dimension matching, specificity calculation, or registry vocabulary checks (SPEC-009 does not apply).
39
+
40
+ **RECOMMENDED:** Authors **SHOULD** treat string names as a temporary escape hatch and track a remediation plan. Each string-named token **SHOULD** eventually be given a structured name object, at which point SPEC-017 no longer fires.
41
+
22
42
  ### Name object
23
43
 
24
44
  The **name object** identifies the token in a structured way. Implementations use it for cascade matching, specificity, and tooling.
@@ -72,16 +92,15 @@ When **`value`** is present, it **MUST** conform to the declared value type for
72
92
 
73
93
  The following OPTIONAL fields implement the token lifecycle model described in [#623](https://github.com/adobe/spectrum-design-data/discussions/623) and the evolution policy in [Evolution](evolution.md). Inspired by Swift's `@available` attribute, Kotlin's `@Deprecated`, and OpenAPI 3.3's deprecation model.
74
94
 
75
- | Field | Type | Description |
76
- | -------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
77
- | `uuid` | string (UUID) | Stable unique id for rename tracking and diffs. |
78
- | `introduced` | string (version) | Spec version when the token was first added (e.g. `"1.0.0"`). |
79
- | `lastModified` | string (version) | Spec version when the token's value or metadata last changed (e.g. `"2.1.0"`). Updated on any non-formatting change. |
80
- | `deprecated` | string (version) | Spec version when the token was deprecated (e.g. `"3.2.0"`). Truthy = deprecated. |
81
- | `deprecated_comment` | string | Human-readable deprecation explanation and migration guidance. |
82
- | `replaced_by` | string (UUID) or array of string (UUID) | UUID(s) of the replacement token(s). Single string for 1:1 replacement; array for one-to-many splits. |
83
- | `plannedRemoval` | string (version) | Spec version when the token will be removed. If omitted, defaults to the next major version after `deprecated`. |
84
- | `private` | boolean | Not part of public API surface. |
95
+ | Field | Type | Description |
96
+ | -------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
97
+ | `uuid` | string (UUID) | Stable unique id for rename tracking and diffs. |
98
+ | `introduced` | string (version) | Spec version when the token was first added (e.g. `"1.0.0"`). |
99
+ | `deprecated` | string (version) | Spec version when the token was deprecated (e.g. `"3.2.0"`). Truthy = deprecated. |
100
+ | `deprecated_comment` | string | Human-readable deprecation explanation and migration guidance. |
101
+ | `replaced_by` | string (UUID) or array of string (UUID) | UUID(s) of the replacement token(s). Single string for 1:1 replacement; array for one-to-many splits. |
102
+ | `plannedRemoval` | string (version) | Spec version when the token will be removed. If omitted, defaults to the next major version after `deprecated`. |
103
+ | `private` | boolean | Not part of public API surface. |
85
104
 
86
105
  #### Lifecycle example
87
106
 
@@ -91,7 +110,6 @@ The following OPTIONAL fields implement the token lifecycle model described in [
91
110
  "value": "#0265dc",
92
111
  "uuid": "aaaaaaaa-0001-4000-8000-000000000001",
93
112
  "introduced": "1.0.0",
94
- "lastModified": "3.2.0",
95
113
  "deprecated": "3.2.0",
96
114
  "deprecated_comment": "Use action-background-default instead.",
97
115
  "replaced_by": "bbbbbbbb-0002-4000-8000-000000000001",
@@ -111,15 +129,13 @@ The following OPTIONAL fields implement the token lifecycle model described in [
111
129
 
112
130
  **NORMATIVE:** Each UUID in `replaced_by` **MUST** resolve to an existing token in the dataset (see rule `SPEC-010`).
113
131
 
114
- **NORMATIVE:** If `lastModified` is present, its version **MUST NOT** precede `introduced` (see rule `SPEC-014`). Authors **SHOULD** bump `lastModified` whenever a token's `value`, alias `$ref`, or non-formatting metadata changes; pure formatting or comment-only edits do not require a bump.
115
-
116
132
  #### Legacy format mapping
117
133
 
118
134
  When generating legacy-format output from cascade tokens:
119
135
 
120
136
  * `deprecated: "3.2.0"` maps to `deprecated: true`
121
137
  * `replaced_by: "<uuid>"` maps to `renamed: "<target-token-name>"` (resolved via UUID lookup)
122
- * `introduced`, `lastModified`, and `plannedRemoval` have no legacy equivalent and are not emitted
138
+ * `introduced` and `plannedRemoval` have no legacy equivalent and are not emitted
123
139
 
124
140
  When migrating legacy-format tokens to cascade:
125
141
 
@@ -153,6 +169,55 @@ Example:
153
169
 
154
170
  Individual types (color, dimension, opacity, etc.) **MUST** be defined as JSON Schemas under `schemas/value-types/` and **MUST** use `$id` under the same `v0` base path as [Overview — JSON Schema `$id`](index.md).
155
171
 
172
+ ### Value-type declaration (`$valueType`)
173
+
174
+ A token **MAY** include a `$valueType` field: a URI reference pointing to a value-type schema under `schemas/value-types/`.
175
+
176
+ ```json
177
+ {
178
+ "name": { "property": "typography-scale", "scale": "desktop" },
179
+ "$valueType": "value-types/typography-scale.schema.json",
180
+ "value": { "fontSize": "14px", "lineHeight": "18px" },
181
+ "uuid": "377145e8-079b-43fd-b522-8f16b1b8f883"
182
+ }
183
+ ```
184
+
185
+ **NORMATIVE:** When `$valueType` is present on a token with a literal `value`, the value **MUST** validate against the referenced schema (rule SPEC-016).
186
+
187
+ **NORMATIVE:** When `$valueType` is present on an alias token (`$ref`), the alias resolution chain **MUST** terminate at a token whose value validates against the same value-type schema (rule SPEC-002, extended).
188
+
189
+ **RECOMMENDED:** All tokens with composite values (see below) **SHOULD** include `$valueType` to enable tooling discovery and validation. Tokens without `$valueType` remain valid under the permissive `anyOf` union in `token.schema.json`.
190
+
191
+ ### Composite value types
192
+
193
+ A **composite value type** is a value-type schema whose root type is `object` or `array`. Composite values bundle multiple sub-values into a single token.
194
+
195
+ **NORMATIVE:** Composite value types **MUST** be defined as JSON Schemas under `schemas/value-types/` following the same conventions as primitive value types.
196
+
197
+ **NORMATIVE:** A composite token participates in cascade resolution as an **atomic unit**. Sub-values within a composite **MUST NOT** independently match, override, or participate in specificity calculation.
198
+
199
+ ### Inline alias references
200
+
201
+ Within a composite value, a string sub-value **MAY** be an **inline alias**: a reference to another token that is resolved to produce the final sub-value.
202
+
203
+ ```json
204
+ {
205
+ "name": { "property": "component-m-regular" },
206
+ "$valueType": "value-types/typography.schema.json",
207
+ "value": {
208
+ "fontFamily": "{sans-serif-font-family}",
209
+ "fontSize": "{font-size-100}",
210
+ "fontWeight": "{regular-font-weight}",
211
+ "letterSpacing": "{letter-spacing}",
212
+ "lineHeight": "{line-height-font-size-100}"
213
+ }
214
+ }
215
+ ```
216
+
217
+ **NORMATIVE:** In legacy format, inline aliases use the syntax `{token-name}` (curly-brace-wrapped token property name). In cascade format, the inline alias syntax is `{token-name}` for backward compatibility; UUID-based inline aliases are reserved for a future spec version.
218
+
219
+ **NORMATIVE:** Inline aliases within composite values are subject to alias resolution rules. Validators **MUST** resolve inline aliases and report errors for missing targets (SPEC-014), type mismatches (SPEC-015), and circular references (SPEC-003, extended).
220
+
156
221
  ## Relationship to legacy Spectrum tokens
157
222
 
158
223
  The current `@adobe/spectrum-tokens` JSON uses **sets** (`color-set`, `scale-set`, …). This specification describes the **target** per-token model. Mapping from legacy to this format is out of scope for this document; see [#743](https://github.com/adobe/spectrum-design-data/issues/743).
@@ -1,10 +0,0 @@
1
- {
2
- "layer": 2,
3
- "errors": [
4
- {
5
- "rule_id": "SPEC-014",
6
- "severity": "error",
7
- "message_pattern": "lastModified .* earlier than introduced"
8
- }
9
- ]
10
- }
@@ -1,9 +0,0 @@
1
- [
2
- {
3
- "name": { "property": "last-modified-before-introduced" },
4
- "value": "#ffffff",
5
- "uuid": "aaaaaaaa-0001-4000-8000-000000000001",
6
- "introduced": "2.0.0",
7
- "lastModified": "1.5.0"
8
- }
9
- ]
@@ -1,12 +0,0 @@
1
- {
2
- "name": {
3
- "component": "button",
4
- "object": "background",
5
- "property": "color",
6
- "variant": "primary"
7
- },
8
- "value": "#0265dc",
9
- "uuid": "aaaaaaaa-0001-4000-8000-000000000001",
10
- "introduced": "1.0.0",
11
- "lastModified": "2.1.0"
12
- }