@adobe/design-data-spec 0.0.1 → 0.1.1
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.
- package/LICENSE +201 -0
- package/conformance/README.md +83 -10
- package/conformance/diff/cross-format/expected.json +27 -0
- package/conformance/diff/cross-format/new/tokens.tokens.json +8 -0
- package/conformance/diff/cross-format/old/tokens.json +7 -0
- package/conformance/diff/deprecated-new-token/expected.json +13 -0
- package/conformance/diff/deprecated-new-token/new/tokens.tokens.json +8 -0
- package/conformance/diff/deprecated-new-token/old/tokens.tokens.json +1 -0
- package/conformance/diff/deprecated-set-level/expected.json +13 -0
- package/conformance/diff/deprecated-set-level/new/tokens.tokens.json +10 -0
- package/conformance/diff/deprecated-set-level/old/tokens.tokens.json +1 -0
- package/conformance/diff/identical-tokens/expected.json +8 -0
- package/conformance/diff/identical-tokens/new/tokens.tokens.json +12 -0
- package/conformance/diff/identical-tokens/old/tokens.tokens.json +12 -0
- package/conformance/diff/matched-gaining-deprecated/expected.json +20 -0
- package/conformance/diff/matched-gaining-deprecated/new/tokens.tokens.json +8 -0
- package/conformance/diff/matched-gaining-deprecated/old/tokens.tokens.json +7 -0
- package/conformance/diff/property-nested-change/expected.json +27 -0
- package/conformance/diff/property-nested-change/new/tokens.tokens.json +10 -0
- package/conformance/diff/property-nested-change/old/tokens.tokens.json +10 -0
- package/conformance/diff/property-value-update/expected.json +21 -0
- package/conformance/diff/property-value-update/new/tokens.tokens.json +7 -0
- package/conformance/diff/property-value-update/old/tokens.tokens.json +7 -0
- package/conformance/diff/rename-by-uuid/expected.json +22 -0
- package/conformance/diff/rename-by-uuid/new/tokens.tokens.json +7 -0
- package/conformance/diff/rename-by-uuid/old/tokens.tokens.json +7 -0
- package/conformance/diff/rename-with-property-changes/expected.json +28 -0
- package/conformance/diff/rename-with-property-changes/new/tokens.tokens.json +7 -0
- package/conformance/diff/rename-with-property-changes/old/tokens.tokens.json +7 -0
- package/conformance/diff/replaced-by-pairing/expected.json +43 -0
- package/conformance/diff/replaced-by-pairing/new/tokens.tokens.json +7 -0
- package/conformance/diff/replaced-by-pairing/old/tokens.tokens.json +10 -0
- package/conformance/diff/reverted-token/expected.json +13 -0
- package/conformance/diff/reverted-token/new/tokens.tokens.json +7 -0
- package/conformance/diff/reverted-token/old/tokens.tokens.json +8 -0
- package/conformance/diff/simple-add-delete/expected.json +18 -0
- package/conformance/diff/simple-add-delete/new/tokens.tokens.json +7 -0
- package/conformance/diff/simple-add-delete/old/tokens.tokens.json +7 -0
- package/conformance/diff/uuid-backfill/expected.json +20 -0
- package/conformance/diff/uuid-backfill/new/tokens.tokens.json +7 -0
- package/conformance/diff/uuid-backfill/old/tokens.tokens.json +6 -0
- package/conformance/invalid/SPEC-008/dimension.json +5 -0
- package/conformance/invalid/SPEC-008/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-008/tokens.tokens.json +15 -0
- package/conformance/invalid/SPEC-010/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-010/tokens.tokens.json +9 -0
- package/conformance/invalid/SPEC-011/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-011/tokens.tokens.json +22 -0
- package/conformance/invalid/SPEC-012/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-012/tokens.tokens.json +13 -0
- package/conformance/invalid/SPEC-013/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-013/tokens.tokens.json +8 -0
- package/conformance/invalid/SPEC-014/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-014/tokens.tokens.json +9 -0
- package/conformance/query/and-conditions/expected.json +1 -0
- package/conformance/query/and-conditions/input/tokens.tokens.json +17 -0
- package/conformance/query/and-conditions/query.txt +1 -0
- package/conformance/query/and-or-precedence/expected.json +1 -0
- package/conformance/query/and-or-precedence/input/tokens.tokens.json +22 -0
- package/conformance/query/and-or-precedence/query.txt +1 -0
- package/conformance/query/empty-matches-all/expected.json +1 -0
- package/conformance/query/empty-matches-all/input/tokens.tokens.json +17 -0
- package/conformance/query/empty-matches-all/query.txt +1 -0
- package/conformance/query/negation/expected.json +1 -0
- package/conformance/query/negation/input/tokens.tokens.json +17 -0
- package/conformance/query/negation/query.txt +1 -0
- package/conformance/query/no-matches/expected.json +1 -0
- package/conformance/query/no-matches/input/tokens.tokens.json +7 -0
- package/conformance/query/no-matches/query.txt +1 -0
- package/conformance/query/or-conditions/expected.json +1 -0
- package/conformance/query/or-conditions/input/tokens.tokens.json +17 -0
- package/conformance/query/or-conditions/query.txt +1 -0
- package/conformance/query/schema-key/expected.json +1 -0
- package/conformance/query/schema-key/input/tokens.tokens.json +14 -0
- package/conformance/query/schema-key/query.txt +1 -0
- package/conformance/query/single-field/expected.json +1 -0
- package/conformance/query/single-field/input/tokens.tokens.json +17 -0
- package/conformance/query/single-field/query.txt +1 -0
- package/conformance/query/wildcard-prefix/expected.json +1 -0
- package/conformance/query/wildcard-prefix/input/tokens.tokens.json +17 -0
- package/conformance/query/wildcard-prefix/query.txt +1 -0
- package/conformance/query/wildcard-suffix/expected.json +1 -0
- package/conformance/query/wildcard-suffix/input/tokens.tokens.json +17 -0
- package/conformance/query/wildcard-suffix/query.txt +1 -0
- package/conformance/resolution/alias-resolved-after-cascade/dimensions/color-scheme.json +5 -0
- package/conformance/resolution/alias-resolved-after-cascade/expected.json +5 -0
- package/conformance/resolution/alias-resolved-after-cascade/input/tokens.tokens.json +12 -0
- package/conformance/resolution/alias-resolved-after-cascade/query.json +4 -0
- package/conformance/resolution/base-fallback/dimensions/color-scheme.json +5 -0
- package/conformance/resolution/base-fallback/expected.json +5 -0
- package/conformance/resolution/base-fallback/input/tokens.tokens.json +7 -0
- package/conformance/resolution/base-fallback/query.json +4 -0
- package/conformance/resolution/specificity-wins/dimensions/color-scheme.json +5 -0
- package/conformance/resolution/specificity-wins/expected.json +5 -0
- package/conformance/resolution/specificity-wins/input/tokens.tokens.json +12 -0
- package/conformance/resolution/specificity-wins/query.json +4 -0
- package/conformance/valid/lifecycle-with-last-modified.json +12 -0
- package/fields/anatomy.json +15 -0
- package/fields/color-scheme.json +15 -0
- package/fields/component.json +15 -0
- package/fields/contrast.json +15 -0
- package/fields/density.json +15 -0
- package/fields/object.json +15 -0
- package/fields/orientation.json +15 -0
- package/fields/position.json +15 -0
- package/fields/property.json +15 -0
- package/fields/scale.json +15 -0
- package/fields/shape.json +15 -0
- package/fields/size.json +15 -0
- package/fields/state.json +15 -0
- package/fields/structure.json +15 -0
- package/fields/substructure.json +15 -0
- package/fields/variant.json +15 -0
- package/package.json +4 -3
- package/rules/rules.yaml +65 -2
- package/schemas/cascade-file.schema.json +10 -0
- package/schemas/field.schema.json +85 -0
- package/schemas/manifest.schema.json +55 -1
- package/schemas/token.schema.json +97 -18
- package/spec/cascade.md +18 -2
- package/spec/diff.md +97 -0
- package/spec/dimensions.md +15 -6
- package/spec/evolution.md +99 -0
- package/spec/index.md +11 -8
- package/spec/manifest.md +18 -1
- package/spec/query.md +135 -0
- package/spec/taxonomy.md +189 -0
- package/spec/token-format.md +118 -19
|
@@ -15,30 +15,73 @@
|
|
|
15
15
|
"$defs": {
|
|
16
16
|
"nameObject": {
|
|
17
17
|
"type": "object",
|
|
18
|
-
"description": "Structured token identity;
|
|
18
|
+
"description": "Structured token identity. Semantic fields describe identity and structure; dimension fields drive cascade resolution. See spec/taxonomy.md and spec/token-format.md. Additional dimension keys MUST be strings.",
|
|
19
19
|
"required": ["property"],
|
|
20
20
|
"properties": {
|
|
21
21
|
"property": {
|
|
22
22
|
"type": "string",
|
|
23
|
-
"minLength": 1
|
|
23
|
+
"minLength": 1,
|
|
24
|
+
"description": "Semantic: the stylistic attribute being defined (e.g. color, width, padding, gap)."
|
|
24
25
|
},
|
|
25
26
|
"component": {
|
|
26
|
-
"type": "string"
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "Semantic: component scope (e.g. button, checkbox)."
|
|
29
|
+
},
|
|
30
|
+
"structure": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "Semantic: reusable visual pattern (e.g. base, container, list). Distinct from component."
|
|
33
|
+
},
|
|
34
|
+
"substructure": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Semantic: child within a parent structure (e.g. item in list-item)."
|
|
37
|
+
},
|
|
38
|
+
"anatomy": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Semantic: visible named part of a component (e.g. handle, icon, label)."
|
|
41
|
+
},
|
|
42
|
+
"object": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "Semantic: styling surface (e.g. background, border, edge, visual)."
|
|
27
45
|
},
|
|
28
46
|
"variant": {
|
|
29
|
-
"type": "string"
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "Semantic: variant within a component (e.g. accent, negative, primary)."
|
|
30
49
|
},
|
|
31
50
|
"state": {
|
|
32
|
-
"type": "string"
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "Semantic: interactive or semantic state (e.g. hover, focus, disabled)."
|
|
53
|
+
},
|
|
54
|
+
"orientation": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"description": "Semantic: direction or order (e.g. vertical, horizontal)."
|
|
57
|
+
},
|
|
58
|
+
"position": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"description": "Semantic: location relative to another object (e.g. affixed, top, bottom)."
|
|
61
|
+
},
|
|
62
|
+
"size": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "Semantic: relative t-shirt sizing (e.g. small, medium, large). Distinct from dimension scale."
|
|
65
|
+
},
|
|
66
|
+
"density": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"description": "Semantic: space within or around component parts (e.g. spacious, compact)."
|
|
69
|
+
},
|
|
70
|
+
"shape": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "Semantic: overall component shape (e.g. uniform)."
|
|
33
73
|
},
|
|
34
74
|
"colorScheme": {
|
|
35
|
-
"type": "string"
|
|
75
|
+
"type": "string",
|
|
76
|
+
"description": "Dimension: color scheme mode (e.g. light, dark, wireframe)."
|
|
36
77
|
},
|
|
37
78
|
"scale": {
|
|
38
|
-
"type": "string"
|
|
79
|
+
"type": "string",
|
|
80
|
+
"description": "Dimension: platform density scale (e.g. desktop, mobile)."
|
|
39
81
|
},
|
|
40
82
|
"contrast": {
|
|
41
|
-
"type": "string"
|
|
83
|
+
"type": "string",
|
|
84
|
+
"description": "Dimension: contrast level (e.g. regular, high)."
|
|
42
85
|
}
|
|
43
86
|
},
|
|
44
87
|
"additionalProperties": {
|
|
@@ -88,20 +131,38 @@
|
|
|
88
131
|
"introduced": {
|
|
89
132
|
"type": "string"
|
|
90
133
|
},
|
|
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
|
+
},
|
|
91
138
|
"deprecated": {
|
|
92
|
-
"type": "
|
|
139
|
+
"type": "string",
|
|
140
|
+
"description": "Spec version when token was deprecated (e.g. \"3.2.0\"). Truthy = deprecated."
|
|
93
141
|
},
|
|
94
142
|
"deprecated_comment": {
|
|
95
143
|
"type": "string"
|
|
96
144
|
},
|
|
97
|
-
"
|
|
98
|
-
"
|
|
145
|
+
"replaced_by": {
|
|
146
|
+
"oneOf": [
|
|
147
|
+
{ "type": "string", "format": "uuid" },
|
|
148
|
+
{
|
|
149
|
+
"type": "array",
|
|
150
|
+
"items": { "type": "string", "format": "uuid" },
|
|
151
|
+
"minItems": 1
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
"description": "UUID(s) of the replacement token(s). Array form requires deprecated_comment."
|
|
99
155
|
},
|
|
100
|
-
"
|
|
101
|
-
"type": "string"
|
|
156
|
+
"plannedRemoval": {
|
|
157
|
+
"type": "string",
|
|
158
|
+
"description": "Spec version when token will be removed."
|
|
102
159
|
},
|
|
103
160
|
"private": {
|
|
104
161
|
"type": "boolean"
|
|
162
|
+
},
|
|
163
|
+
"description": {
|
|
164
|
+
"type": "string",
|
|
165
|
+
"description": "Plain text describing the token's purpose (aligns with DTCG $description)."
|
|
105
166
|
}
|
|
106
167
|
},
|
|
107
168
|
"additionalProperties": false
|
|
@@ -131,20 +192,38 @@
|
|
|
131
192
|
"introduced": {
|
|
132
193
|
"type": "string"
|
|
133
194
|
},
|
|
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
|
+
},
|
|
134
199
|
"deprecated": {
|
|
135
|
-
"type": "
|
|
200
|
+
"type": "string",
|
|
201
|
+
"description": "Spec version when token was deprecated (e.g. \"3.2.0\"). Truthy = deprecated."
|
|
136
202
|
},
|
|
137
203
|
"deprecated_comment": {
|
|
138
204
|
"type": "string"
|
|
139
205
|
},
|
|
140
|
-
"
|
|
141
|
-
"
|
|
206
|
+
"replaced_by": {
|
|
207
|
+
"oneOf": [
|
|
208
|
+
{ "type": "string", "format": "uuid" },
|
|
209
|
+
{
|
|
210
|
+
"type": "array",
|
|
211
|
+
"items": { "type": "string", "format": "uuid" },
|
|
212
|
+
"minItems": 1
|
|
213
|
+
}
|
|
214
|
+
],
|
|
215
|
+
"description": "UUID(s) of the replacement token(s). Array form requires deprecated_comment."
|
|
142
216
|
},
|
|
143
|
-
"
|
|
144
|
-
"type": "string"
|
|
217
|
+
"plannedRemoval": {
|
|
218
|
+
"type": "string",
|
|
219
|
+
"description": "Spec version when token will be removed."
|
|
145
220
|
},
|
|
146
221
|
"private": {
|
|
147
222
|
"type": "boolean"
|
|
223
|
+
},
|
|
224
|
+
"description": {
|
|
225
|
+
"type": "string",
|
|
226
|
+
"description": "Plain text describing the token's purpose (aligns with DTCG $description)."
|
|
148
227
|
}
|
|
149
228
|
},
|
|
150
229
|
"additionalProperties": false
|
package/spec/cascade.md
CHANGED
|
@@ -26,7 +26,12 @@ Design data is organized in three layers, ordered from lowest to highest precede
|
|
|
26
26
|
|
|
27
27
|
**NORMATIVE:** When two candidates from the **same layer** match the context, the candidate with **higher** specificity **MUST** win.
|
|
28
28
|
|
|
29
|
-
**NORMATIVE:** Ties on layer and specificity **MUST** be broken by
|
|
29
|
+
**NORMATIVE:** Ties on layer and specificity **MUST** be broken by **document order**:
|
|
30
|
+
|
|
31
|
+
1. Within a single source file, the token that appears **earlier** in the array **MUST** win.
|
|
32
|
+
2. Across multiple files, the file with the **lexicographically earlier path** within the dataset **MUST** win.
|
|
33
|
+
|
|
34
|
+
**NORMATIVE:** Validators **MUST** emit a SPEC-006 warning when a tie is detected, as ties indicate potential authoring mistakes.
|
|
30
35
|
|
|
31
36
|
## Context
|
|
32
37
|
|
|
@@ -40,10 +45,19 @@ The following outline is **RECOMMENDED** for conforming resolvers:
|
|
|
40
45
|
2. Filter to candidates at or below the requested layer.
|
|
41
46
|
3. Select the maximum **layer** precedence.
|
|
42
47
|
4. Within that layer, select the maximum **specificity**.
|
|
43
|
-
5. Break remaining ties by
|
|
48
|
+
5. Break remaining ties by document order (earlier in file wins; lexicographically earlier file path wins across files). Emit SPEC-006 warning.
|
|
49
|
+
6. If the winning candidate is an alias (`$ref`), **resolve the alias chain** to a literal value (see [Alias resolution](#alias-resolution)).
|
|
44
50
|
|
|
45
51
|
Exact matching rules for omitted dimensions are defined alongside dimension declarations in [Dimensions](dimensions.md).
|
|
46
52
|
|
|
53
|
+
## Alias resolution
|
|
54
|
+
|
|
55
|
+
**NORMATIVE:** Alias (`$ref`) resolution **MUST** occur **after** cascade selection. The resolution algorithm selects the winning candidate using layer, specificity, and tie-breaking rules before any alias chain is followed.
|
|
56
|
+
|
|
57
|
+
**NORMATIVE:** If the winning candidate is an alias, its `$ref` chain **MUST** be resolved to a literal value using the standard alias-resolution rules (SPEC-001 through SPEC-003 in `rules/rules.yaml`). The alias target is resolved within the same dataset (i.e. the same merged layer stack), not relative to any single layer.
|
|
58
|
+
|
|
59
|
+
**RATIONALE:** Aliases participate in cascade as opaque references — their target values are not examined during specificity calculation or layer comparison. This keeps resolution deterministic and allows aliases to be valid candidates at any specificity level.
|
|
60
|
+
|
|
47
61
|
## Cross-dimensional overrides
|
|
48
62
|
|
|
49
63
|
**NORMATIVE:** Overrides that combine multiple dimensions in a way not expressible by a single name object alone **MUST** use **explicit combination tokens** (tokens whose name object sets multiple non-default dimensions) as defined in the dataset; magic merging of unrelated tokens is **NOT** allowed.
|
|
@@ -52,3 +66,5 @@ Exact matching rules for omitted dimensions are defined alongside dimension decl
|
|
|
52
66
|
|
|
53
67
|
* [#646 — Token Schema Structure and Validation System](https://github.com/adobe/spectrum-design-data/discussions/646)
|
|
54
68
|
* [#714 — Design Data Specification](https://github.com/adobe/spectrum-design-data/discussions/714)
|
|
69
|
+
* [#757 — Phase 2: Cascade tie-breaking rule](https://github.com/adobe/spectrum-design-data/issues/757)
|
|
70
|
+
* [#758 — Phase 2: Alias resolution ordering in cascade context](https://github.com/adobe/spectrum-design-data/issues/758)
|
package/spec/diff.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Semantic diff
|
|
2
|
+
|
|
3
|
+
**Spec version:** `1.0.0-draft` (see [Overview](index.md))
|
|
4
|
+
|
|
5
|
+
This document defines the **semantic diff model**: how two versions of a design data dataset are compared to produce a structured change report with awareness of renames, deprecations, and property-level changes.
|
|
6
|
+
|
|
7
|
+
## Token identity
|
|
8
|
+
|
|
9
|
+
A **token identity** determines whether a token in the old dataset and a token in the new dataset refer to the same logical token.
|
|
10
|
+
|
|
11
|
+
**NORMATIVE:** Implementations **MUST** use the following matching rules, in order:
|
|
12
|
+
|
|
13
|
+
1. **UUID match** — If a token in the old dataset and a token in the new dataset share the same `uuid` value, they are the **same token** regardless of name.
|
|
14
|
+
2. **Name-object equivalence** — When a UUID match is not found for a token — because the old token, the new token, or both lack a `uuid`, or because no counterpart with the matching `uuid` exists in the other dataset — two tokens are the same if their `name` objects are deeply equal (all fields present with identical values).
|
|
15
|
+
3. **Replacement link** — When passes 1 and 2 leave an old token unpaired and it carries a `replaced_by` field whose UUID matches an unpaired new token, the pair is established. This enables diff classification as **renamed** for tokens that were deprecated with a machine-readable replacement pointer.
|
|
16
|
+
|
|
17
|
+
**NORMATIVE:** UUID matching **MUST** take precedence over name-object equivalence, which **MUST** take precedence over replacement link matching. A UUID match always identifies the token pair, even if name objects differ (which constitutes a rename).
|
|
18
|
+
|
|
19
|
+
**RATIONALE:** UUID-based identity allows tokens to be renamed without breaking continuity tracking. Name-object equivalence is a fallback for legacy datasets that predate UUID adoption. Replacement link matching is a tertiary fallback for deprecated tokens that carry an explicit `replaced_by` UUID pointing to their successor.
|
|
20
|
+
|
|
21
|
+
## Change taxonomy
|
|
22
|
+
|
|
23
|
+
A semantic diff classifies every token into exactly one of six categories:
|
|
24
|
+
|
|
25
|
+
| Category | Definition |
|
|
26
|
+
| -------------- | ------------------------------------------------------------------------------------------------ |
|
|
27
|
+
| **renamed** | Token exists in both datasets (matched by identity) but the name has changed. |
|
|
28
|
+
| **deprecated** | Token exists only in the new dataset (unmatched) and carries a `deprecated` field. |
|
|
29
|
+
| **reverted** | Token existed with `deprecated` in the old dataset and no longer carries it in the new dataset. |
|
|
30
|
+
| **added** | Token exists only in the new dataset and is not renamed, deprecated, or pre-existing. |
|
|
31
|
+
| **deleted** | Token exists only in the old dataset and is not the source of a rename. |
|
|
32
|
+
| **updated** | Token exists in both datasets (matched by identity) with the same name but changed properties. |
|
|
33
|
+
|
|
34
|
+
**NORMATIVE:** Every token that appears in the old dataset, the new dataset, or both **MUST** be classified into exactly one category. Categories are mutually exclusive.
|
|
35
|
+
|
|
36
|
+
## Category partitioning
|
|
37
|
+
|
|
38
|
+
**NORMATIVE:** Implementations **MUST** resolve categories in the following order to ensure mutual exclusivity:
|
|
39
|
+
|
|
40
|
+
1. **Renamed** — Identify all identity-matched pairs where the name has changed. These tokens are removed from further classification as added or deleted.
|
|
41
|
+
2. **Deprecated** — Among remaining unmatched new tokens, identify those carrying a `deprecated` field. These are removed from the "added" pool.
|
|
42
|
+
3. **Reverted** — Among identity-matched pairs, identify tokens where `deprecated` was present in the old version and absent in the new. These are removed from the "updated" pool.
|
|
43
|
+
4. **Added** — Remaining unmatched new tokens that are not renamed, deprecated, or pre-existing in the old dataset.
|
|
44
|
+
5. **Deleted** — Remaining unmatched old tokens that are not the source of a rename.
|
|
45
|
+
6. **Updated** — Remaining identity-matched pairs with unchanged names but differing properties.
|
|
46
|
+
|
|
47
|
+
**RATIONALE:** This ordering mirrors the pipeline in existing tooling and ensures that a renamed token does not also appear as "added" + "deleted", a deprecated token does not appear as "added", and so forth. A matched token that newly gains a `deprecated` field is classified as **updated** — the deprecation surfaces as a property-level change in the updated sub-categories, not as a new-token deprecation.
|
|
48
|
+
|
|
49
|
+
## Deprecation normalization
|
|
50
|
+
|
|
51
|
+
**NORMATIVE:** When a token uses the legacy `sets` structure and **all** set entries carry `deprecated: true`, the token **MUST** be treated as deprecated at the token level for diff classification purposes, even if the top-level token object does not carry `deprecated`.
|
|
52
|
+
|
|
53
|
+
**RATIONALE:** Set-level deprecation that covers all variants is semantically equivalent to token-level deprecation. Normalizing this prevents diff noise from implementation-level differences in where the `deprecated` flag is placed.
|
|
54
|
+
|
|
55
|
+
## Property-level changes
|
|
56
|
+
|
|
57
|
+
For tokens classified as **updated** (or **renamed** with additional property changes), a semantic diff **SHOULD** produce property-level change records.
|
|
58
|
+
|
|
59
|
+
### Change record format
|
|
60
|
+
|
|
61
|
+
Each property-level change is described by:
|
|
62
|
+
|
|
63
|
+
| Field | Type | Description |
|
|
64
|
+
| ---------------- | ------ | ------------------------------------------------------- |
|
|
65
|
+
| `path` | string | Dot-separated path from the token root (e.g. `value`, `name.colorScheme`, `sets.light.value`). |
|
|
66
|
+
| `new_value` | any | The value in the new dataset. Present for additions and updates. |
|
|
67
|
+
| `original_value` | any | The value in the old dataset. Present for deletions and updates. |
|
|
68
|
+
|
|
69
|
+
### Property change sub-categories
|
|
70
|
+
|
|
71
|
+
| Sub-category | Condition |
|
|
72
|
+
| --------------------- | ------------------------------------------------- |
|
|
73
|
+
| **added-properties** | Property exists in new but not in old. |
|
|
74
|
+
| **deleted-properties**| Property exists in old but not in new. |
|
|
75
|
+
| **updated-properties**| Property exists in both but with different values. |
|
|
76
|
+
|
|
77
|
+
**NORMATIVE:** Property comparison **MUST** be recursive: nested objects are traversed and changes reported at the leaf level with full dot-separated paths.
|
|
78
|
+
|
|
79
|
+
**NORMATIVE:** Property comparison **MUST** use deep equality for values. Two values are equal if their JSON serializations are identical.
|
|
80
|
+
|
|
81
|
+
## Output ordering
|
|
82
|
+
|
|
83
|
+
**RECOMMENDED:** Diff output **SHOULD** be deterministic. Within each category, tokens **SHOULD** be sorted by their canonical name (or new name for renames) in lexicographic order.
|
|
84
|
+
|
|
85
|
+
**RATIONALE:** Deterministic output makes diff reports suitable for snapshot testing and human review.
|
|
86
|
+
|
|
87
|
+
## Cross-format compatibility
|
|
88
|
+
|
|
89
|
+
**NORMATIVE:** A conforming diff engine **MUST** accept both legacy format (JSON object maps) and cascade format (JSON arrays with `.tokens.json` extension) as inputs for either the old or new dataset, including mixed-format comparisons (e.g. legacy old, cascade new).
|
|
90
|
+
|
|
91
|
+
**RATIONALE:** The diff operates on the token graph abstraction, which normalizes both formats into the same `TokenRecord` structure. This enables diffing across format migrations without special-case handling.
|
|
92
|
+
|
|
93
|
+
## References
|
|
94
|
+
|
|
95
|
+
* [#623 — Token Lifecycle Metadata](https://github.com/adobe/spectrum-design-data/discussions/623)
|
|
96
|
+
* [#714 — Design Data Specification](https://github.com/adobe/spectrum-design-data/discussions/714)
|
|
97
|
+
* [#776 — Phase 3: Diff change taxonomy and token identity rules](https://github.com/adobe/spectrum-design-data/issues/776)
|
package/spec/dimensions.md
CHANGED
|
@@ -25,13 +25,21 @@ A **dimension declaration** is a JSON object describing one axis of variation. I
|
|
|
25
25
|
|
|
26
26
|
## Built-in dimensions
|
|
27
27
|
|
|
28
|
-
These dimensions **SHOULD** be used consistently across Spectrum-compatible datasets:
|
|
28
|
+
These dimensions are declared in the `dimensions/` catalog (see [Dimension catalog](#dimension-catalog)) and **SHOULD** be used consistently across Spectrum-compatible datasets:
|
|
29
29
|
|
|
30
|
-
| `name` |
|
|
31
|
-
| ------------- |
|
|
32
|
-
| `colorScheme` | `light`, `dark`, `wireframe
|
|
33
|
-
| `scale` | `
|
|
34
|
-
| `contrast` | `regular`, `high
|
|
30
|
+
| `name` | `modes` | `default` | Notes |
|
|
31
|
+
| ------------- | ---------------------------- | --------- | --------------------------------------------------------------------------------- |
|
|
32
|
+
| `colorScheme` | `light`, `dark`, `wireframe` | `light` | Theme / appearance. |
|
|
33
|
+
| `scale` | `desktop`, `mobile` | `desktop` | Density scale. Legacy names; desktop = medium, mobile = large in W3C terminology. |
|
|
34
|
+
| `contrast` | `regular`, `high` | `regular` | Accessibility contrast level. |
|
|
35
|
+
|
|
36
|
+
## Dimension catalog
|
|
37
|
+
|
|
38
|
+
The Spectrum foundation publishes dimension declarations as JSON files under `packages/design-data-spec/dimensions/`. Each file conforms to [`dimension.schema.json`](../schemas/dimension.schema.json).
|
|
39
|
+
|
|
40
|
+
**NORMATIVE:** Tooling (validators, resolution engine) **MUST** load dimension declarations from the dataset’s dimension catalog before performing specificity calculations or coverage validation.
|
|
41
|
+
|
|
42
|
+
**RECOMMENDED:** The catalog directory is named `dimensions/` and is co-located with the dataset’s spec package or manifest.
|
|
35
43
|
|
|
36
44
|
## Optional dimensions
|
|
37
45
|
|
|
@@ -53,3 +61,4 @@ Additional dimensions (e.g. `language`, `motion`) **MAY** be declared in a datas
|
|
|
53
61
|
|
|
54
62
|
* [#646 — Token Schema Structure and Validation System](https://github.com/adobe/spectrum-design-data/discussions/646)
|
|
55
63
|
* [#714 — Design Data Specification](https://github.com/adobe/spectrum-design-data/discussions/714)
|
|
64
|
+
* [#746 — Phase 2: Dimension declarations (machine-readable)](https://github.com/adobe/spectrum-design-data/issues/746)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Evolution
|
|
2
|
+
|
|
3
|
+
**Spec version:** `1.0.0-draft` (see [Overview](index.md))
|
|
4
|
+
|
|
5
|
+
This document defines the **evolution policy** for the Design Data Specification: how the spec, schemas, and token data change over time, with a focus on the token deprecation lifecycle and backward compatibility.
|
|
6
|
+
|
|
7
|
+
## Token deprecation lifecycle
|
|
8
|
+
|
|
9
|
+
Tokens progress through the following lifecycle stages:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
introduced → active → deprecated → planned removal → removed
|
|
13
|
+
```
|
|
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. |
|
|
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. |
|
|
22
|
+
|
|
23
|
+
### Lifecycle fields
|
|
24
|
+
|
|
25
|
+
See [Token format — Lifecycle and metadata](token-format.md#lifecycle-and-metadata) for the full field definitions and normative rules.
|
|
26
|
+
|
|
27
|
+
### What `replaced_by` guarantees
|
|
28
|
+
|
|
29
|
+
When a token carries `replaced_by`:
|
|
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.
|
|
35
|
+
|
|
36
|
+
## Migration windows
|
|
37
|
+
|
|
38
|
+
**NORMATIVE:** A deprecated token **MUST** remain in the published dataset for at least **two minor versions** after the version recorded in `deprecated` before it may be removed.
|
|
39
|
+
|
|
40
|
+
**EXAMPLE:** A token deprecated in `3.2.0` may not be removed until `3.4.0` at the earliest. Removal in a major version (e.g. `4.0.0`) is always permitted regardless of how recently the token was deprecated.
|
|
41
|
+
|
|
42
|
+
**RATIONALE:** Two minor versions gives consumers at least two release cycles to migrate. The major-version escape hatch allows accumulated deprecations to be cleaned up in a coordinated breaking release.
|
|
43
|
+
|
|
44
|
+
If `plannedRemoval` is set, it overrides the default window — the token will be removed in the specified version (which must not precede the `deprecated` version).
|
|
45
|
+
|
|
46
|
+
## Change classification
|
|
47
|
+
|
|
48
|
+
The specification follows [Semantic Versioning](https://semver.org/) for its published artifacts (schemas, rule catalog, spec documents).
|
|
49
|
+
|
|
50
|
+
### Minor changes (backward compatible)
|
|
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
|
|
58
|
+
|
|
59
|
+
### Major changes (breaking)
|
|
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)
|
|
67
|
+
|
|
68
|
+
### Patch changes (clarifications)
|
|
69
|
+
|
|
70
|
+
* Typo fixes in spec prose
|
|
71
|
+
* Clarifications to normative text that do not change conformance behavior
|
|
72
|
+
* Test fixture additions or corrections
|
|
73
|
+
|
|
74
|
+
## Legacy format contract
|
|
75
|
+
|
|
76
|
+
The `@adobe/spectrum-tokens` package continues to publish tokens in the **legacy format** (JSON object maps with `color-set`, `scale-set`, etc.) for backward compatibility with existing consumers.
|
|
77
|
+
|
|
78
|
+
**NORMATIVE:** The legacy format output **MUST** be generated from the cascade-format authoritative source. It is a **derived artifact**, not independently authored.
|
|
79
|
+
|
|
80
|
+
### Lifecycle field mapping
|
|
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) |
|
|
90
|
+
|
|
91
|
+
### Coexistence during migration
|
|
92
|
+
|
|
93
|
+
Both formats are published simultaneously. The cascade format is the source of truth; the legacy format is generated from it. This dual-format period continues until platform consumers have migrated to the cascade format or to platform SDKs that consume it.
|
|
94
|
+
|
|
95
|
+
## References
|
|
96
|
+
|
|
97
|
+
* [#623 — Token Lifecycle Metadata](https://github.com/adobe/spectrum-design-data/discussions/623)
|
|
98
|
+
* [#735 — RFC: Versioning and Evolution](https://github.com/adobe/spectrum-design-data/discussions/735)
|
|
99
|
+
* [#736 — Define spec evolution policy and migration contract](https://github.com/adobe/spectrum-design-data/issues/736)
|
package/spec/index.md
CHANGED
|
@@ -10,14 +10,13 @@ This document is the top-level overview for the **Design Data Specification**: a
|
|
|
10
10
|
The specification defines:
|
|
11
11
|
|
|
12
12
|
1. **Token format** — structured token identity (`name`), literal `value` or alias `$ref`, and lifecycle metadata ([Token format](token-format.md)).
|
|
13
|
-
2. **
|
|
14
|
-
3. **
|
|
15
|
-
4. **
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* Full evolution, migration windows, and SDK version negotiation — see [discussion #735](https://github.com/adobe/spectrum-design-data/discussions/735).
|
|
13
|
+
2. **Taxonomy** — concept categories, token term vocabulary, formatting style, and the distinction between component anatomy and token objects ([Taxonomy](taxonomy.md)).
|
|
14
|
+
3. **Cascade and resolution** — layered data (foundation, platform, product), specificity, and how a context picks a winning value ([Cascade](cascade.md)).
|
|
15
|
+
4. **Dimensions** — declared modes, defaults, and coverage expectations ([Dimensions](dimensions.md)).
|
|
16
|
+
5. **Platform manifest** — how a platform repo pins foundation data, filters tokens, and applies typed overrides ([Manifest](manifest.md)).
|
|
17
|
+
6. **Semantic diff** — change taxonomy, token identity rules, and property-level change tracking for comparing dataset versions ([Diff](diff.md)).
|
|
18
|
+
7. **Query notation** — filter syntax for selecting tokens by structured fields ([Query](query.md)).
|
|
19
|
+
8. **Evolution** — deprecation lifecycle, migration windows, change classification, and legacy format contract ([Evolution](evolution.md)).
|
|
21
20
|
|
|
22
21
|
## Conformance
|
|
23
22
|
|
|
@@ -54,9 +53,13 @@ Full governance (compatibility tiers, migration, CLI `--spec-version`) is discus
|
|
|
54
53
|
| Document | Role |
|
|
55
54
|
| ------------------------------- | ---------------------------------------------------------------- |
|
|
56
55
|
| [Token format](token-format.md) | Token `name`, `value` / `$ref`, value types, lifecycle metadata. |
|
|
56
|
+
| [Taxonomy](taxonomy.md) | Concept categories, vocabulary, formatting, anatomy vs objects. |
|
|
57
57
|
| [Cascade](cascade.md) | Layers, specificity, resolution algorithm. |
|
|
58
58
|
| [Dimensions](dimensions.md) | Dimension declarations, built-in dimensions, coverage. |
|
|
59
59
|
| [Manifest](manifest.md) | Platform manifest fields and validation expectations. |
|
|
60
|
+
| [Diff](diff.md) | Semantic diff change taxonomy, token identity, property changes. |
|
|
61
|
+
| [Query](query.md) | Filter notation for selecting tokens by structured fields. |
|
|
62
|
+
| [Evolution](evolution.md) | Deprecation lifecycle, migration windows, change classification. |
|
|
60
63
|
|
|
61
64
|
## JSON Schema `$id` and versioning
|
|
62
65
|
|
package/spec/manifest.md
CHANGED
|
@@ -26,7 +26,9 @@ 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.
|
|
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.
|
|
30
32
|
|
|
31
33
|
### `overrides`
|
|
32
34
|
|
|
@@ -38,6 +40,21 @@ Each override object **MUST** include enough information to identify a target to
|
|
|
38
40
|
|
|
39
41
|
**RECOMMENDED:** `extensions` follows the same structural conventions as foundation token files (tokens, dimensions) and **SHOULD** be validated with the same Layer 1 and Layer 2 rules.
|
|
40
42
|
|
|
43
|
+
#### `extensions.formatting`
|
|
44
|
+
|
|
45
|
+
A platform **MAY** declare formatting rules that control how structured name objects are serialized into flat token name strings for that platform. See [Taxonomy — Platform formatting configuration](taxonomy.md#platform-formatting-configuration) for motivation and examples.
|
|
46
|
+
|
|
47
|
+
| Field | Type | Description |
|
|
48
|
+
| --------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `conceptOrder` | array of string | Ordered list of name object field names for serialization. Each entry **MUST** be a declared field name from the design system's [field catalog](../fields/) (see [Token format — Name object](token-format.md#name-object)). Omitted fields are appended in the default order defined by each field declaration's `serialization.position` (see [Taxonomy — Default serialization](taxonomy.md#default-serialization-legacy-format)). |
|
|
50
|
+
| `casing` | string | One of: `kebab-case`, `camelCase`, `PascalCase`, `SCREAMING_SNAKE_CASE`. Default: `kebab-case`. |
|
|
51
|
+
| `delimiter` | string | Character(s) separating concepts in the serialized string (e.g. `-`, `_`, `.`, `/`). Default: `-`. |
|
|
52
|
+
| `abbreviations` | object | Map of full term → abbreviated form (e.g. `{ "background": "bg" }`). Abbreviations are applied after concept ordering and before casing. |
|
|
53
|
+
|
|
54
|
+
**NORMATIVE:** When `extensions.formatting` is absent, the default serialization defined in [Taxonomy](taxonomy.md#default-serialization-legacy-format) is used.
|
|
55
|
+
|
|
56
|
+
**NORMATIVE:** A formatter applying `extensions.formatting` **MUST** produce deterministic output — the same name object and formatting configuration **MUST** always yield the same string.
|
|
57
|
+
|
|
41
58
|
## Validation
|
|
42
59
|
|
|
43
60
|
**NORMATIVE:** Manifests **MUST** pass Layer 1 JSON Schema validation.
|