@adobe/design-data-spec 4.1.0 → 4.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.
Files changed (41) hide show
  1. package/conformance/manifest-extensions/README.md +4 -1
  2. package/conformance/manifest-extensions/invalid/mode-set-missing-default/expected-errors.json +7 -0
  3. package/conformance/manifest-extensions/invalid/mode-set-missing-default/extensions/mode-sets/broken.json +1 -0
  4. package/conformance/manifest-extensions/invalid/mode-set-missing-default/manifest.json +4 -0
  5. package/conformance/manifest-extensions/invalid/mode-set-remove-mode-of-default/expected-errors.json +6 -0
  6. package/conformance/manifest-extensions/invalid/mode-set-remove-mode-of-default/extensions/mode-sets/remove-desktop.json +1 -0
  7. package/conformance/manifest-extensions/invalid/mode-set-remove-mode-of-default/extensions/mode-sets/scale.json +1 -0
  8. package/conformance/manifest-extensions/invalid/mode-set-remove-mode-of-default/manifest.json +1 -0
  9. package/conformance/manifest-extensions/invalid/mode-set-set-default-nonmember/expected-errors.json +6 -0
  10. package/conformance/manifest-extensions/invalid/mode-set-set-default-nonmember/extensions/mode-sets/default-tv.json +1 -0
  11. package/conformance/manifest-extensions/invalid/mode-set-set-default-nonmember/extensions/mode-sets/scale.json +1 -0
  12. package/conformance/manifest-extensions/invalid/mode-set-set-default-nonmember/manifest.json +1 -0
  13. package/conformance/manifest-extensions/invalid/mode-set-target-not-found/expected-errors.json +6 -0
  14. package/conformance/manifest-extensions/invalid/mode-set-target-not-found/extensions/mode-sets/add-tv.json +1 -0
  15. package/conformance/manifest-extensions/invalid/mode-set-target-not-found/manifest.json +1 -0
  16. package/conformance/manifest-extensions/invalid/mode-set-unknown-op/expected-errors.json +6 -0
  17. package/conformance/manifest-extensions/invalid/mode-set-unknown-op/extensions/mode-sets/bogus.json +1 -0
  18. package/conformance/manifest-extensions/invalid/mode-set-unknown-op/extensions/mode-sets/scale.json +1 -0
  19. package/conformance/manifest-extensions/invalid/mode-set-unknown-op/manifest.json +1 -0
  20. package/conformance/manifest-extensions/valid/injects-mode-set/expected.json +1 -0
  21. package/conformance/manifest-extensions/valid/injects-mode-set/extensions/mode-sets/interface-level.json +5 -0
  22. package/conformance/manifest-extensions/valid/injects-mode-set/manifest.json +4 -0
  23. package/conformance/manifest-extensions/valid/mode-set-add-mode/expected.json +8 -0
  24. package/conformance/manifest-extensions/valid/mode-set-add-mode/extensions/mode-sets/add-tv.json +1 -0
  25. package/conformance/manifest-extensions/valid/mode-set-add-mode/extensions/mode-sets/scale.json +1 -0
  26. package/conformance/manifest-extensions/valid/mode-set-add-mode/manifest.json +1 -0
  27. package/conformance/manifest-extensions/valid/mode-set-remove/expected.json +1 -0
  28. package/conformance/manifest-extensions/valid/mode-set-remove/extensions/mode-sets/contrast.json +1 -0
  29. package/conformance/manifest-extensions/valid/mode-set-remove/extensions/mode-sets/remove-contrast.json +1 -0
  30. package/conformance/manifest-extensions/valid/mode-set-remove/manifest.json +1 -0
  31. package/conformance/manifest-extensions/valid/mode-set-remove-mode/expected.json +8 -0
  32. package/conformance/manifest-extensions/valid/mode-set-remove-mode/extensions/mode-sets/color-scheme.json +5 -0
  33. package/conformance/manifest-extensions/valid/mode-set-remove-mode/extensions/mode-sets/remove-wireframe.json +1 -0
  34. package/conformance/manifest-extensions/valid/mode-set-remove-mode/manifest.json +1 -0
  35. package/conformance/manifest-extensions/valid/mode-set-set-default/expected.json +6 -0
  36. package/conformance/manifest-extensions/valid/mode-set-set-default/extensions/mode-sets/default-tv.json +1 -0
  37. package/conformance/manifest-extensions/valid/mode-set-set-default/extensions/mode-sets/scale.json +1 -0
  38. package/conformance/manifest-extensions/valid/mode-set-set-default/manifest.json +1 -0
  39. package/package.json +1 -1
  40. package/spec/manifest.md +61 -18
  41. package/spec/mode-sets.md +10 -0
@@ -4,7 +4,7 @@ Exercises the Layer 2 platform manifest's `extensions/` **directory** loader
4
4
  (`packages/design-data-spec/spec/manifest.md#extensions-directory`): a sibling
5
5
  `extensions/` tree (or a custom name via the manifest's `extensionsDir` field)
6
6
  whose category subdirectories (`tokens/`, `components/`, `fields/`,
7
- `guidelines/`, `platform-extensions/`, `relationships/`) are discovered,
7
+ `guidelines/`, `mode-sets/`, `platform-extensions/`, `relationships/`) are discovered,
8
8
  glob+merged in sorted path order, per-fragment schema-validated, and spliced
9
9
  into the manifest before it's applied to the graph.
10
10
 
@@ -23,6 +23,7 @@ into the manifest before it's applied to the graph.
23
23
  },
24
24
  "components": { "present": ["tab-bar-ios"] },
25
25
  "fields": { "present": ["hapticStyle"] },
26
+ "modeSets": { "present": ["interfaceLevel"] },
26
27
  "guidelines": { "present": ["ios-haptics"] },
27
28
  "tokens": { "orderByUuid": ["<uuid-1>", "<uuid-2>"] }
28
29
  }
@@ -42,6 +43,7 @@ Rust SDK drives these fixtures in `sdk/core/src/lib.rs` via the
42
43
  | `valid/injects-platform-extension` | A `platform-extensions/` fragment is injected. |
43
44
  | `valid/injects-field` | A `fields/` fragment is injected. |
44
45
  | `valid/injects-guideline` | A `guidelines/` fragment is injected. |
46
+ | `valid/injects-mode-set` | A `mode-sets/` fragment is injected (declare-or-replace by name). |
45
47
  | `valid/injects-relationship` | A `relationships/` plain-add fragment is injected. |
46
48
  | `valid/override-relationship-by-uuid` | An `op: "override"` entry replaces a plain add sharing its `uuid`, regardless of file sort order. |
47
49
  | `valid/plain-add-uuid-collision-append` | Two plain adds sharing a `uuid` both append rather than one overwriting the other. |
@@ -55,6 +57,7 @@ Rust SDK drives these fixtures in `sdk/core/src/lib.rs` via the
55
57
  | `invalid/component-missing-name` | A `components/` fragment missing `name` fails Layer 1 fragment schema validation. |
56
58
  | `invalid/field-invalid-kind` | A `fields/` fragment with an invalid `kind` enum value fails schema validation. |
57
59
  | `invalid/guideline-invalid-category` | A `guidelines/` fragment with an invalid `category` enum value fails schema validation. |
60
+ | `invalid/mode-set-missing-default` | A `mode-sets/` fragment missing the required `default` field fails Layer 1 fragment schema validation. |
58
61
  | `invalid/platform-extension-missing-extends` | A `platform-extensions/` fragment missing `extends` fails schema validation. |
59
62
  | `invalid/token-invalid-uuid` | A `tokens/*.tokens.json` fragment with a malformed `uuid` fails schema validation. |
60
63
  | `invalid/relationship-missing-scope` | A `relationships/` plain-add fragment missing `scope` fails schema validation. |
@@ -0,0 +1,7 @@
1
+ {
2
+ "errors": [
3
+ { "message_pattern": "broken\\.json" },
4
+ { "message_pattern": "mode-set\\.schema\\.json" },
5
+ { "message_pattern": "\"default\"" }
6
+ ]
7
+ }
@@ -0,0 +1 @@
1
+ { "name": "interfaceLevel", "modes": ["base", "elevated"] }
@@ -0,0 +1,4 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0"
4
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "errors": [
3
+ { "message_pattern": "removeMode" },
4
+ { "message_pattern": "current default" }
5
+ ]
6
+ }
@@ -0,0 +1 @@
1
+ { "name": "scale", "op": "removeMode", "mode": "desktop" }
@@ -0,0 +1 @@
1
+ { "name": "scale", "modes": ["desktop", "mobile"], "default": "desktop" }
@@ -0,0 +1 @@
1
+ { "specVersion": "1.0.0-draft", "foundationVersion": "1.0.0" }
@@ -0,0 +1,6 @@
1
+ {
2
+ "errors": [
3
+ { "message_pattern": "setDefault" },
4
+ { "message_pattern": "not in its modes" }
5
+ ]
6
+ }
@@ -0,0 +1 @@
1
+ { "name": "scale", "op": "setDefault", "default": "tv" }
@@ -0,0 +1 @@
1
+ { "name": "scale", "modes": ["desktop", "mobile"], "default": "desktop" }
@@ -0,0 +1 @@
1
+ { "specVersion": "1.0.0-draft", "foundationVersion": "1.0.0" }
@@ -0,0 +1,6 @@
1
+ {
2
+ "errors": [
3
+ { "message_pattern": "\"scale\"" },
4
+ { "message_pattern": "does not exist" }
5
+ ]
6
+ }
@@ -0,0 +1 @@
1
+ { "name": "scale", "op": "addMode", "mode": "tv" }
@@ -0,0 +1 @@
1
+ { "specVersion": "1.0.0-draft", "foundationVersion": "1.0.0" }
@@ -0,0 +1,6 @@
1
+ {
2
+ "errors": [
3
+ { "message_pattern": "unknown op" },
4
+ { "message_pattern": "\"bogus\"" }
5
+ ]
6
+ }
@@ -0,0 +1 @@
1
+ { "name": "scale", "modes": ["desktop", "mobile"], "default": "desktop" }
@@ -0,0 +1 @@
1
+ { "specVersion": "1.0.0-draft", "foundationVersion": "1.0.0" }
@@ -0,0 +1 @@
1
+ { "modeSets": { "present": ["interfaceLevel"] } }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "interfaceLevel",
3
+ "modes": ["base", "elevated"],
4
+ "default": "base"
5
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0"
4
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "modeSets": {
3
+ "present": ["scale"],
4
+ "modes": {
5
+ "scale": { "modes": ["desktop", "mobile", "tv"], "default": "desktop" }
6
+ }
7
+ }
8
+ }
@@ -0,0 +1 @@
1
+ { "name": "scale", "op": "addMode", "mode": "tv" }
@@ -0,0 +1 @@
1
+ { "name": "scale", "modes": ["desktop", "mobile"], "default": "desktop" }
@@ -0,0 +1 @@
1
+ { "specVersion": "1.0.0-draft", "foundationVersion": "1.0.0" }
@@ -0,0 +1 @@
1
+ { "modeSets": { "absent": ["contrast"] } }
@@ -0,0 +1 @@
1
+ { "name": "contrast", "modes": ["regular", "high"], "default": "regular" }
@@ -0,0 +1 @@
1
+ { "specVersion": "1.0.0-draft", "foundationVersion": "1.0.0" }
@@ -0,0 +1,8 @@
1
+ {
2
+ "modeSets": {
3
+ "present": ["colorScheme"],
4
+ "modes": {
5
+ "colorScheme": { "modes": ["light", "dark"], "default": "light" }
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "colorScheme",
3
+ "modes": ["light", "dark", "wireframe"],
4
+ "default": "light"
5
+ }
@@ -0,0 +1 @@
1
+ { "name": "colorScheme", "op": "removeMode", "mode": "wireframe" }
@@ -0,0 +1 @@
1
+ { "specVersion": "1.0.0-draft", "foundationVersion": "1.0.0" }
@@ -0,0 +1,6 @@
1
+ {
2
+ "modeSets": {
3
+ "present": ["scale"],
4
+ "modes": { "scale": { "default": "tv" } }
5
+ }
6
+ }
@@ -0,0 +1 @@
1
+ { "name": "scale", "op": "setDefault", "default": "tv" }
@@ -0,0 +1 @@
1
+ { "name": "scale", "modes": ["desktop", "mobile", "tv"], "default": "desktop" }
@@ -0,0 +1 @@
1
+ { "specVersion": "1.0.0-draft", "foundationVersion": "1.0.0" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/design-data-spec",
3
- "version": "4.1.0",
3
+ "version": "4.2.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/spec/manifest.md CHANGED
@@ -8,23 +8,25 @@ This document defines the **platform manifest**: how a platform implementation r
8
8
 
9
9
  The manifest supports a fixed, enumerated set of operations against the foundation — it does **not** allow overriding, aliasing, or removing arbitrary foundation artifacts. Support is concentrated on tokens; translations and schemas have no manifest-level override mechanism at all.
10
10
 
11
- | Operation | Supported? | Field | Applies to |
12
- | -------------------------------------------------- | ------------------------------------------------------------ | --------------------------------- | --------------------- |
13
- | Remove / exclude | Yes | `exclude` | Tokens only |
14
- | Include / whitelist | Yes | `include` | Tokens only |
15
- | Override value (type-preserving) | Yes | `overrides[].value` | Tokens only |
16
- | Override → re-alias | Yes | `overrides[].$ref` | Tokens only |
17
- | Add new tokens (may alias via `$ref`) | Yes | `extensions/tokens/` | Tokens |
18
- | Add / replace components | Yes | `extensions/components/` | Components |
19
- | Add / replace field declarations | Yes | `extensions/fields/` | Fields |
20
- | Add / replace guideline documents | Yes | `extensions/guidelines/` | Guidelines |
21
- | Add relationships/CTRs; override/remove by `uuid` | Yes | `extensions/relationships/` | Relationships (CTRs) |
22
- | Add / remove naming exceptions | Yes | `namingExceptions` | Naming validation |
23
- | Annotate existing terminology (cannot add new ids) | Yes | `extensions/platform-extensions/` | Existing registry ids |
24
- | Restrict allowed mode-set values | Yes | `modeSetRestrictions` | Mode sets |
25
- | Reformat name serialization | Schema-declared only, not yet applied by the reference SDK | `formatting` | Token name strings |
26
- | Override/remove/alias translations | No | — | — |
27
- | Override Layer-1 schemas | No (decided; see [spike](manifest-schema-override-spike.md)) | — | — |
11
+ | Operation | Supported? | Field | Applies to |
12
+ | --------------------------------------------------- | ------------------------------------------------------------ | --------------------------------- | --------------------- |
13
+ | Remove / exclude | Yes | `exclude` | Tokens only |
14
+ | Include / whitelist | Yes | `include` | Tokens only |
15
+ | Override value (type-preserving) | Yes | `overrides[].value` | Tokens only |
16
+ | Override → re-alias | Yes | `overrides[].$ref` | Tokens only |
17
+ | Add new tokens (may alias via `$ref`) | Yes | `extensions/tokens/` | Tokens |
18
+ | Add / replace components | Yes | `extensions/components/` | Components |
19
+ | Add / replace field declarations | Yes | `extensions/fields/` | Fields |
20
+ | Add / replace guideline documents | Yes | `extensions/guidelines/` | Guidelines |
21
+ | Declare / replace platform-local mode set | Yes | `extensions/mode-sets/` | Mode sets |
22
+ | Add / remove / retarget a single mode; remove a set | Yes | `extensions/mode-sets/` (`op`) | Mode sets |
23
+ | Add relationships/CTRs; override/remove by `uuid` | Yes | `extensions/relationships/` | Relationships (CTRs) |
24
+ | Add / remove naming exceptions | Yes | `namingExceptions` | Naming validation |
25
+ | Annotate existing terminology (cannot add new ids) | Yes | `extensions/platform-extensions/` | Existing registry ids |
26
+ | Restrict allowed mode-set values | Yes | `modeSetRestrictions` | Mode sets |
27
+ | Reformat name serialization | Schema-declared only, not yet applied by the reference SDK | `formatting` | Token name strings |
28
+ | Override/remove/alias translations | No | — | — |
29
+ | Override Layer-1 schemas | No (decided; see [spike](manifest-schema-override-spike.md)) | — | — |
28
30
 
29
31
  ## Manifest document
30
32
 
@@ -86,6 +88,7 @@ extensions/
86
88
  tokens/ *.tokens.json cascade-format token files
87
89
  components/ <component>.json one component per file
88
90
  fields/ <field>.json one field declaration per file
91
+ mode-sets/ <mode-set>.json one mode set per file
89
92
  relationships/ <component>.json one CTR set per file
90
93
  guidelines/ <topic>.json one guideline per file
91
94
  platform-extensions/ <platform>-<registry>.json
@@ -103,7 +106,7 @@ or empty subdirectory contributes nothing and is not an error.
103
106
  cascade token file) and **MUST** validate against `cascade-file.schema.json`. Files are
104
107
  **deep-merged** into one tokens object, in sorted path order. Entries **MAY** carry a `$ref`
105
108
  to alias an existing token instead of a literal value.
106
- * **`components/`, `fields/`, `guidelines/`, `platform-extensions/`** — one artifact per file.
109
+ * **`components/`, `fields/`, `guidelines/`, `mode-sets/`, `platform-extensions/`** — one artifact per file.
107
110
  Entries across all files in the subdirectory are concatenated, in sorted path order, and
108
111
  injected into the corresponding catalog **add-or-replace by name** (for
109
112
  `platform-extensions/`, by `termId`; see below). When two files declare the same name,
@@ -115,6 +118,7 @@ load time — this is enforced reference-SDK behavior, not an aspirational goal:
115
118
  * **`components/`** → `component.schema.json`
116
119
  * **`fields/`** → `field.schema.json` (see [`extensions/fields/`](#extensionsfields) below)
117
120
  * **`guidelines/`** → `guideline.schema.json`
121
+ * **`mode-sets/`** → `mode-set.schema.json` (see [`extensions/mode-sets/`](#extensionsmode-sets) below)
118
122
  * **`relationships/`** → `relationship.schema.json` (see the Add/Override/remove rules above)
119
123
  * **`platform-extensions/`** → `platform-extension.json` (see
120
124
  [`extensions/platform-extensions/`](#extensionsplatform-extensions) below)
@@ -143,6 +147,45 @@ declared) references field names by string — a platform that renames or remove
143
147
  also references there is self-inconsistent; that is a manifest-authoring concern, not enforced
144
148
  by the reference SDK.
145
149
 
150
+ #### `extensions/mode-sets/`
151
+
152
+ Platform-local mode-set declarations and edits, injected into the mode-set catalog for this
153
+ platform's resolution. A mode set's `name` is its stable key (there is no separate `uuid`, unlike
154
+ relationships) — every entry, add or op alike, identifies its target by `name`:
155
+
156
+ * **Add / replace:** an entry with no `op` field is a full mode-set object (**NORMATIVE:** MUST
157
+ validate against `mode-set.schema.json`, requiring `name`, `modes`, `default`). A `name` not
158
+ already in the foundation catalog is a clean new platform axis (e.g. `interfaceLevel: [base,
159
+ elevated]`); a `name` matching a foundation mode set replaces it, for this platform only — the
160
+ foundation catalog is untouched.
161
+ * **`"op": "addMode"`** — appends one mode value (`mode`) to an existing set, without restating its
162
+ other modes. **NORMATIVE:** the target set (by `name`) MUST already exist and MUST NOT already
163
+ contain `mode`; either condition failing is a manifest error.
164
+ * **`"op": "removeMode"`** — drops one mode value (`mode`) from an existing set. **NORMATIVE:** the
165
+ target MUST exist, MUST contain `mode`, `mode` MUST NOT be the set's current default (retarget the
166
+ default first with `setDefault`), the set MUST have more than one mode remaining afterward, and no
167
+ token in the resolved graph MAY still carry `mode` as that mode set's value — each violation is a
168
+ manifest error. (This mirrors the reference SDK's authoring-time `remove_mode` guard, which checks
169
+ the same "still referenced" condition against cascade token files.)
170
+ * **`"op": "setDefault"`** — retargets an existing set's default mode (`default`). **NORMATIVE:**
171
+ the target MUST exist and `default` MUST already be one of its modes.
172
+ * **`"op": "remove"`** — drops the whole set (by `name`) from this platform's resolution.
173
+ **NORMATIVE:** the target MUST exist, and no token in the resolved graph MAY still carry this mode
174
+ set's `name` as a key — either violation is a manifest error.
175
+
176
+ Add/replace entries are applied before op entries, regardless of file sort order, so a set declared
177
+ in one file can be edited by an op in another file within the same `extensions/mode-sets/`
178
+ directory. Ops within each group apply in sorted path order. Any other `op` value, or an op entry
179
+ missing its required field (`mode` for `addMode`/`removeMode`, `default` for `setDefault`), is a
180
+ manifest error — the reference SDK rejects the load rather than silently skipping it, consistent
181
+ with `extensions/relationships/`'s override/remove ops above. `default ∈ modes` remains a Layer 2
182
+ concern for the resolved graph as a whole, enforced by SPEC-005 against whichever mode sets end up
183
+ there, foundation or platform-declared alike.
184
+
185
+ Contrast with `modeSetRestrictions` ([Mode Sets — Platform restrictions](mode-sets.md#platform-restrictions)), a top-level manifest field that only *narrows* the allowed values of an
186
+ existing mode set at resolution time — it cannot add, remove, or retarget a mode value, or declare
187
+ a new axis.
188
+
146
189
  #### `extensions/platform-extensions/`
147
190
 
148
191
  Platform terminology annotations layered onto **existing** foundation registry entries (for
package/spec/mode-sets.md CHANGED
@@ -45,6 +45,16 @@ The Spectrum foundation publishes mode set declarations as JSON files under `pac
45
45
 
46
46
  Additional mode sets (e.g. `language`, `motion`) **MAY** be declared in a dataset's mode set catalog. Token name objects **MAY** include keys matching declared mode set names.
47
47
 
48
+ A platform manifest **MAY** also declare or edit mode sets local to that platform, one entry per
49
+ file under `extensions/mode-sets/` (see [Platform manifest — `extensions/`
50
+ directory](manifest.md#extensions-directory)). An entry with no `op` uses **declare-or-replace-by-name** semantics, same as `extensions/fields/`: a new `name` adds a platform-local axis (e.g.
51
+ `interfaceLevel`), while a `name` matching a foundation mode set replaces it for that platform only,
52
+ leaving the foundation catalog untouched. `op: "addMode"` / `"removeMode"` / `"setDefault"` /
53
+ `"remove"` instead edit an existing set in place — adding or dropping a single mode value,
54
+ retargeting its default, or dropping the whole set — without restating its other modes. This is
55
+ distinct from `modeSetRestrictions` (below), which only *narrows* the allowed values of an existing
56
+ mode set at resolution time rather than editing the declared set itself.
57
+
48
58
  ## Defaults and specificity
49
59
 
50
60
  **NORMATIVE:** A token name object **omitting** a mode set field implies the token applies under the mode set's **`default`** mode for specificity and matching purposes unless the spec for that mode set states otherwise.