@adobe/design-data-spec 4.0.0 → 4.1.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.
@@ -79,14 +79,14 @@ These fixtures verify the field-mapping contract in [Evolution — Legacy format
79
79
  | Folder | Intent |
80
80
  | --------------------------- | -------------------------------------------------------------------------------------------------- |
81
81
  | `generation/flat-token` | A single cascade token with no mode sets maps to a single keyed entry `{ slug: { value, uuid } }`. |
82
- | `generation/mode-set-token` | Multiple cascade tokens sharing a `set_uuid` merge into a single `sets`-keyed entry. |
82
+ | `generation/mode-set-token` | Multiple cascade tokens sharing a `conceptId` merge into a single `sets`-keyed entry. |
83
83
 
84
84
  The Rust SDK drives these fixtures via `design-data migrate legacy-output` (see `sdk/core/src/legacy.rs`).
85
85
  Consumer implementations MUST regenerate each `input/` and diff against `expected/` to claim conformance
86
86
  with the output-generator determinism contract.
87
87
 
88
88
  See [`generation/README.md`](generation/README.md) for the fixture format, the run command, and
89
- documentation of the `set_uuid`/`set_schema` cascade grouping fields used in `mode-set-token`.
89
+ documentation of the `conceptId`/`set_schema` cascade grouping fields used in `mode-set-token`.
90
90
 
91
91
  ***
92
92
 
@@ -38,20 +38,20 @@ design-data migrate legacy-output <case>/input --output <case>/expected
38
38
  | Case | What it exercises |
39
39
  | ------------------ | ----------------------------------------------------------------------------------------------------------- |
40
40
  | `flat-token` | Basic flat token conversion (property key derivation, field passthrough) |
41
- | `mode-set-token` | Color-set reconstruction from per-mode cascade records (`set_uuid`/`set_schema`) |
41
+ | `mode-set-token` | Color-set reconstruction from per-mode cascade records (`conceptId`/`set_schema`) |
42
42
  | `deprecated-token` | `deprecated: "version"` string → `true` boolean; `deprecated_comment` passthrough; `plannedRemoval` dropped |
43
43
  | `renamed-token` | `replaced_by: "<uuid>"` → `renamed: "<property-name>"` via global UUID→name map |
44
44
  | `alias-rewire` | `$ref: "<uuid>"` → `value: "{<property-name>}"` (alias denormalization with UUID resolution) |
45
45
  | `mode-set-edit` | `deprecated`/`renamed` hoisted to outer set level when consistent across all mode entries |
46
46
 
47
- ## Cascade grouping fields (`set_uuid` / `set_schema`)
47
+ ## Cascade grouping fields (`conceptId` / `set_schema`)
48
48
 
49
49
  The `mode-set-token` and `mode-set-edit` fixtures use two cascade-internal grouping fields:
50
50
 
51
- | Field | Type | Role |
52
- | ------------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
- | `set_uuid` | string | Shared UUID that groups per-mode cascade records into one set-keyed legacy entry. The SDK registers this as `set_uuid → child-key list` in `sdk/core/src/graph.rs` and the legacy generator collapses all records sharing a `set_uuid` into a single `sets`-keyed output object. |
54
- | `set_schema` | string | The `$schema` URI of the aggregate set token emitted at the outer (non-mode-keyed) level in the legacy output (e.g. `color-set.json`). |
51
+ | Field | Type | Role |
52
+ | ------------ | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
+ | `conceptId` | string | Canonical concept-level UUID shared by every mode row of a design concept. The SDK registers this as `conceptId → child-key list` in `sdk/core/src/graph.rs` and the legacy generator collapses all records sharing a `conceptId` into a single `sets`-keyed output object. |
54
+ | `set_schema` | string | The `$schema` URI of the aggregate set token emitted at the outer (non-mode-keyed) level in the legacy output (e.g. `color-set.json`). |
55
55
 
56
56
  These fields are not yet in the normative token-format field table — they are Rust SDK
57
57
  implementation fields defined in `sdk/core/src/graph.rs` and emitted by the cascade ingest
@@ -9,7 +9,7 @@
9
9
  "$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color.json",
10
10
  "value": "rgb(20, 115, 230)",
11
11
  "uuid": "11111111-0001-4000-8000-000000000001",
12
- "set_uuid": "11111111-0003-4000-8000-000000000003",
12
+ "conceptId": "11111111-0003-4000-8000-000000000003",
13
13
  "set_schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color-set.json",
14
14
  "lifecycle": {
15
15
  "deprecatedIn": true,
@@ -26,7 +26,7 @@
26
26
  "$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color.json",
27
27
  "value": "rgb(56, 145, 254)",
28
28
  "uuid": "11111111-0002-4000-8000-000000000002",
29
- "set_uuid": "11111111-0003-4000-8000-000000000003",
29
+ "conceptId": "11111111-0003-4000-8000-000000000003",
30
30
  "set_schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color-set.json",
31
31
  "lifecycle": {
32
32
  "deprecatedIn": true,
@@ -38,7 +38,7 @@
38
38
  "$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color.json",
39
39
  "value": "rgb(20, 115, 230)",
40
40
  "uuid": "11111111-0004-4000-8000-000000000004",
41
- "set_uuid": "11111111-0005-4000-8000-000000000005",
41
+ "conceptId": "11111111-0005-4000-8000-000000000005",
42
42
  "set_schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color-set.json"
43
43
  }
44
44
  ]
@@ -9,7 +9,7 @@
9
9
  "$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color.json",
10
10
  "value": "rgb(245, 249, 255)",
11
11
  "uuid": "bbbbbbbb-0001-4000-8000-000000000001",
12
- "set_uuid": "cccccccc-0001-4000-8000-000000000001",
12
+ "conceptId": "cccccccc-0001-4000-8000-000000000001",
13
13
  "set_schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color-set.json",
14
14
  "private": true
15
15
  },
@@ -23,7 +23,7 @@
23
23
  "$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color.json",
24
24
  "value": "rgb(14, 23, 63)",
25
25
  "uuid": "bbbbbbbb-0002-4000-8000-000000000002",
26
- "set_uuid": "cccccccc-0001-4000-8000-000000000001",
26
+ "conceptId": "cccccccc-0001-4000-8000-000000000001",
27
27
  "set_schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color-set.json",
28
28
  "private": true
29
29
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "A set-level alias ($ref targeting a set_uuid) MUST resolve to the mode-appropriate child. Under colorScheme=dark, accent-color-900 → blue-900 dark (different value from light).",
2
+ "description": "A set-level alias ($ref targeting a conceptId) MUST resolve to the mode-appropriate child. Under colorScheme=dark, accent-color-900 → blue-900 dark (different value from light).",
3
3
  "resolved": true,
4
4
  "expected_value": "rgb(75, 117, 255)",
5
5
  "expected_chain_length": 3,
@@ -9,7 +9,7 @@
9
9
  "$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color.json",
10
10
  "value": "rgb(59, 99, 251)",
11
11
  "uuid": "ref-blue900-light-0000-000000000002",
12
- "set_uuid": "ref-blue900-set-000-0000-000000000002"
12
+ "conceptId": "ref-blue900-set-000-0000-000000000002"
13
13
  },
14
14
  {
15
15
  "name": {
@@ -21,7 +21,7 @@
21
21
  "$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color.json",
22
22
  "value": "rgb(75, 117, 255)",
23
23
  "uuid": "ref-blue900-dark-0000-000000000002",
24
- "set_uuid": "ref-blue900-set-000-0000-000000000002"
24
+ "conceptId": "ref-blue900-set-000-0000-000000000002"
25
25
  },
26
26
  {
27
27
  "name": { "property": "accent-color-900" },
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "A set-level alias ($ref targeting a set_uuid) MUST resolve to the mode-appropriate child. Under colorScheme=light, accent-color-900 → blue-900 light.",
2
+ "description": "A set-level alias ($ref targeting a conceptId) MUST resolve to the mode-appropriate child. Under colorScheme=light, accent-color-900 → blue-900 light.",
3
3
  "resolved": true,
4
4
  "expected_value": "rgb(59, 99, 251)",
5
5
  "expected_chain_length": 3,
@@ -9,7 +9,7 @@
9
9
  "$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color.json",
10
10
  "value": "rgb(59, 99, 251)",
11
11
  "uuid": "ref-blue900-light-0000-000000000001",
12
- "set_uuid": "ref-blue900-set-000-0000-000000000001"
12
+ "conceptId": "ref-blue900-set-000-0000-000000000001"
13
13
  },
14
14
  {
15
15
  "name": {
@@ -21,7 +21,7 @@
21
21
  "$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/token-types/color.json",
22
22
  "value": "rgb(75, 117, 255)",
23
23
  "uuid": "ref-blue900-dark-0000-000000000001",
24
- "set_uuid": "ref-blue900-set-000-0000-000000000001"
24
+ "conceptId": "ref-blue900-set-000-0000-000000000001"
25
25
  },
26
26
  {
27
27
  "name": { "property": "accent-color-900" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/design-data-spec",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "description": "Design Data Specification — prose, JSON Schemas, rule catalog, and conformance fixtures for the design data format",
5
5
  "type": "module",
6
6
  "repository": {
package/rules/rules.yaml CHANGED
@@ -707,3 +707,20 @@ rules:
707
707
  message: "Relationship $ref {ref} resolves to a token deprecated in {deprecatedIn}"
708
708
  spec_ref: spec/relationship-format.md#legacy-fields-interim
709
709
  introduced_in: "1.0.0-draft"
710
+
711
+ - id: SPEC-059
712
+ name: alias-mode-context-compatibility
713
+ severity: warning
714
+ layer: 2
715
+ category: reference-integrity
716
+ note: >
717
+ Mode analogue of SPEC-002. A `$ref` target that is a `conceptId` (see
718
+ token-format.md#concept-identity-conceptid) is context-resolved elsewhere and exempt
719
+ here; only a direct per-value target is checked.
720
+ assert: >
721
+ An alias whose name object sets a mode-set field (e.g. `colorScheme: dark`) SHOULD NOT
722
+ resolve, via its `$ref` chain, to a per-value token that sets the same field to a
723
+ different value. Tokens whose name object omits the field (mode-agnostic) are exempt.
724
+ message: "Alias {token} sets {field}={ownValue} but its $ref chain resolves to a token with {field}={leafValue}"
725
+ spec_ref: spec/cascade.md#alias-resolution
726
+ introduced_in: "1.0.0-draft"
package/spec/cascade.md CHANGED
@@ -57,7 +57,9 @@ Exact matching rules for omitted mode sets are defined alongside mode set declar
57
57
 
58
58
  **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. In cascade format, `$ref` holds the **UUID** of the target token (see [`token-format.md` §Alias](token-format.md#alias-ref)); resolution proceeds UUID-first, then direct graph key (legacy slug), then serialised name.
59
59
 
60
- **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
+ **NORMATIVE:** When a `$ref` target is a **`conceptId`** (see [`token-format.md` §Concept identity](token-format.md#concept-identity-conceptid)) rather than a per-row `uuid`, resolution **MUST** pick the mode row registered under that `conceptId` whose name-object fields best match the referencing token's own resolution context (greatest count of matching mode-set key/value pairs). Ties **MUST** be broken by the lexicographically smallest `uuid` among equally-scoring rows, so repeated resolution of the same alias always returns the same token. This context-aware step runs in place of, not in addition to, the UUID-first/direct-key/name fallback above — a `conceptId` target either resolves to its best-matching row or (when no rows are registered under it) falls through to the standard chain.
61
+
62
+ **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. Resolving a `conceptId` target against the *referencing* token's own context (rather than always the first or an arbitrary row) is what lets one alias correctly track a multi-mode concept (e.g. a semantic color that must stay light/dark-aligned with whatever aliases to it) without one alias per mode.
61
63
 
62
64
  ## Cross-mode-set overrides
63
65
 
@@ -128,6 +128,34 @@ When **`$ref`** is present, the token is an **alias**. The value **MUST** be a n
128
128
  "uuid": "f24eb871-6419-4cef-88a2-cca8548ae31e" }
129
129
  ```
130
130
 
131
+ ### Concept identity (`conceptId`)
132
+
133
+ **`conceptId`** is a UUID shared by every mode row of the same design concept — e.g. the light,
134
+ dark, and wireframe rows of one color, or the mobile/desktop rows of one scale. It is additive to
135
+ the per-value `uuid` (which remains the `$ref`/diff/`replacedBy`/sync anchor for one specific mode
136
+ row) and, unlike `uuid`, is stable across the whole concept rather than one row.
137
+
138
+ ```json
139
+ // Two mode rows of the same concept share one conceptId, each with its own uuid.
140
+ { "name": { "property": "accent-color", "colorScheme": "light" },
141
+ "value": "rgb(59, 99, 251)",
142
+ "uuid": "aaaaaaaa-0000-4000-8000-000000000001",
143
+ "conceptId": "cccccccc-0000-4000-8000-000000000099" }
144
+ { "name": { "property": "accent-color", "colorScheme": "dark" },
145
+ "value": "rgb(75, 117, 255)",
146
+ "uuid": "aaaaaaaa-0000-4000-8000-000000000002",
147
+ "conceptId": "cccccccc-0000-4000-8000-000000000099" }
148
+ ```
149
+
150
+ **NORMATIVE:** All mode rows of the same design concept **MUST** carry the same `conceptId`.
151
+
152
+ **NORMATIVE:** A `$ref` target **MAY** be a `conceptId` instead of a per-row `uuid`. When it is,
153
+ resolution **MUST** pick the mode row registered under that `conceptId` whose name-object fields
154
+ best match the referencing token's own resolution context (see
155
+ [Cascade resolution — Alias resolution](cascade.md#alias-resolution)), rather than an arbitrary
156
+ row — this is what makes a single alias correctly track light/dark or mobile/desktop without one
157
+ alias per mode.
158
+
131
159
  ### Literal `value`
132
160
 
133
161
  When **`value`** is present, it **MUST** conform to the declared value type for that token. Value types are defined under `schemas/value-types/` and referenced from the token schema.