@adobe/design-data-spec 4.3.0 → 4.5.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 (40) hide show
  1. package/conformance/manifest-extensions/README.md +47 -31
  2. package/conformance/manifest-extensions/base/dataset.json +36 -3
  3. package/conformance/manifest-extensions/invalid/implementations-remove-no-match/expected-errors.json +10 -0
  4. package/conformance/manifest-extensions/invalid/implementations-remove-no-match/extensions/implementations/button.json +9 -0
  5. package/conformance/manifest-extensions/invalid/implementations-remove-no-match/manifest.json +5 -0
  6. package/conformance/manifest-extensions/invalid/implementations-row-missing-reference/expected-errors.json +7 -0
  7. package/conformance/manifest-extensions/invalid/implementations-row-missing-reference/extensions/implementations/button.json +9 -0
  8. package/conformance/manifest-extensions/invalid/implementations-row-missing-reference/manifest.json +5 -0
  9. package/conformance/manifest-extensions/invalid/implementations-unknown-component/expected-errors.json +10 -0
  10. package/conformance/manifest-extensions/invalid/implementations-unknown-component/extensions/implementations/nope.json +12 -0
  11. package/conformance/manifest-extensions/invalid/implementations-unknown-component/manifest.json +5 -0
  12. package/conformance/manifest-extensions/invalid/implementations-unknown-op/expected-errors.json +7 -0
  13. package/conformance/manifest-extensions/invalid/implementations-unknown-op/extensions/implementations/button.json +9 -0
  14. package/conformance/manifest-extensions/invalid/implementations-unknown-op/manifest.json +5 -0
  15. package/conformance/manifest-extensions/valid/implementations-later-file-wins/expected.json +19 -0
  16. package/conformance/manifest-extensions/valid/implementations-later-file-wins/extensions/implementations/a-button.json +11 -0
  17. package/conformance/manifest-extensions/valid/implementations-later-file-wins/extensions/implementations/b-button.json +11 -0
  18. package/conformance/manifest-extensions/valid/implementations-later-file-wins/manifest.json +5 -0
  19. package/conformance/manifest-extensions/valid/implementations-other-owner-untouched/expected.json +26 -0
  20. package/conformance/manifest-extensions/valid/implementations-other-owner-untouched/extensions/implementations/button.json +18 -0
  21. package/conformance/manifest-extensions/valid/implementations-other-owner-untouched/manifest.json +5 -0
  22. package/conformance/manifest-extensions/valid/implementations-platform-local-component/expected.json +12 -0
  23. package/conformance/manifest-extensions/valid/implementations-platform-local-component/extensions/components/tab-bar-ios.json +9 -0
  24. package/conformance/manifest-extensions/valid/implementations-platform-local-component/extensions/implementations/tab-bar-ios.json +10 -0
  25. package/conformance/manifest-extensions/valid/implementations-platform-local-component/manifest.json +5 -0
  26. package/conformance/manifest-extensions/valid/implementations-remove-row/expected.json +11 -0
  27. package/conformance/manifest-extensions/valid/implementations-remove-row/extensions/implementations/button.json +10 -0
  28. package/conformance/manifest-extensions/valid/implementations-remove-row/manifest.json +5 -0
  29. package/conformance/manifest-extensions/valid/implementations-upsert-foundation-row/expected.json +19 -0
  30. package/conformance/manifest-extensions/valid/implementations-upsert-foundation-row/extensions/implementations/button.json +12 -0
  31. package/conformance/manifest-extensions/valid/implementations-upsert-foundation-row/manifest.json +5 -0
  32. package/package.json +2 -1
  33. package/schemas/component.schema.json +8 -3
  34. package/schemas/implementation-mapping.schema.json +69 -0
  35. package/schemas/manifest.schema.json +20 -0
  36. package/spec/authoring-workflow.md +1 -0
  37. package/spec/component-format.md +43 -20
  38. package/spec/index.md +2 -0
  39. package/spec/manifest.md +101 -28
  40. package/spec/taxonomy.md +2 -0
@@ -4,12 +4,14 @@ 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/`, `mode-sets/`, `platform-extensions/`, `relationships/`) are discovered,
7
+ `guidelines/`, `mode-sets/`, `platform-extensions/`, `relationships/`,
8
+ `implementations/`) are discovered,
8
9
  glob+merged in sorted path order, per-fragment schema-validated, and spliced
9
10
  into the manifest before it's applied to the graph.
10
11
 
11
12
  * `base/dataset.json` — a shared minimal seed graph (three foundation tokens:
12
- `btn-bg`, `btn-fg`, `chk-bg` on the `button`/`checkbox` components), loaded
13
+ `btn-bg`, `btn-fg`, `chk-bg` on the `button`/`checkbox` components, plus a
14
+ foundation `button` component with two default `implementations` rows), loaded
13
15
  once per case before that case's `manifest.json` is applied via
14
16
  `apply_configured`.
15
17
  * `valid/<case>/` — `manifest.json` + an `extensions/` (or custom-named) tree
@@ -19,13 +21,18 @@ into the manifest before it's applied to the graph.
19
21
  {
20
22
  "relationships": {
21
23
  "byUuid": { "<uuid>": { "count": 1, "value": "8px" } },
22
- "absent": ["<uuid>"]
24
+ "absent": ["<uuid>"],
23
25
  },
24
26
  "components": { "present": ["tab-bar-ios"] },
25
27
  "fields": { "present": ["hapticStyle"] },
26
28
  "modeSets": { "present": ["interfaceLevel"] },
27
29
  "guidelines": { "present": ["ios-haptics"] },
28
- "tokens": { "orderByUuid": ["<uuid-1>", "<uuid-2>"] }
30
+ "tokens": { "orderByUuid": ["<uuid-1>", "<uuid-2>"] },
31
+ "implementations": {
32
+ "<component>": [
33
+ /* exact merged rows */
34
+ ],
35
+ },
29
36
  }
30
37
  ```
31
38
  A case that only needs to prove "loads clean" uses `{}`.
@@ -37,30 +44,39 @@ Rust SDK drives these fixtures in `sdk/core/src/lib.rs` via the
37
44
  `manifest_extensions_conformance` (pass/fail + error message matching) and
38
45
  `manifest_extensions_behavior` (post-apply graph predicates) test modules.
39
46
 
40
- | Case | Intent |
41
- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
42
- | `valid/injects-component` | A `components/` fragment is injected as a Platform-layer component. |
43
- | `valid/injects-platform-extension` | A `platform-extensions/` fragment is injected. |
44
- | `valid/injects-field` | A `fields/` fragment is injected. |
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). |
47
- | `valid/injects-relationship` | A `relationships/` plain-add fragment is injected. |
48
- | `valid/override-relationship-by-uuid` | An `op: "override"` entry replaces a plain add sharing its `uuid`, regardless of file sort order. |
49
- | `valid/plain-add-uuid-collision-append` | Two plain adds sharing a `uuid` both append rather than one overwriting the other. |
50
- | `valid/remove-relationship-by-uuid` | An `op: "remove"` entry deletes the matching relationship. |
51
- | `valid/tokens-sorted-order` | Multiple `tokens/*.tokens.json` files concatenate in sorted path order. |
52
- | `valid/later-file-wins` | Two `components/` fragments declaring the same component name dedupe to one, later file wins. |
53
- | `valid/custom-extensions-dir` | The manifest's `extensionsDir` field is honored in place of the default `extensions/` name. |
54
- | `valid/override-entry-skips-schema-validation` | An `op: "override"` relationship entry is not schema-validated against `relationship.schema.json` (only plain adds are). |
55
- | `invalid/override-missing-uuid` | An `op: "override"` relationship entry missing `uuid` fails loudly. |
56
- | `invalid/unknown-term-id` | A `platform-extensions/` fragment referencing a non-existent `termId` fails loudly. |
57
- | `invalid/component-missing-name` | A `components/` fragment missing `name` fails Layer 1 fragment schema validation. |
58
- | `invalid/field-invalid-kind` | A `fields/` fragment with an invalid `kind` enum value fails schema validation. |
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. |
61
- | `invalid/platform-extension-missing-extends` | A `platform-extensions/` fragment missing `extends` fails schema validation. |
62
- | `invalid/token-invalid-uuid` | A `tokens/*.tokens.json` fragment with a malformed `uuid` fails schema validation. |
63
- | `invalid/relationship-missing-scope` | A `relationships/` plain-add fragment missing `scope` fails schema validation. |
64
- | `invalid/extensions-dir-parent-traversal` | `extensionsDir` containing a `..` component is rejected. |
65
- | `invalid/extensions-dir-absolute-path` | An absolute `extensionsDir` path is rejected. |
66
- | `invalid/unknown-subdir-name` | An unrecognized subdirectory directly under `extensions/` (not one of the six known categories) is rejected by name. |
47
+ | Case | Intent |
48
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
49
+ | `valid/injects-component` | A `components/` fragment is injected as a Platform-layer component. |
50
+ | `valid/injects-platform-extension` | A `platform-extensions/` fragment is injected. |
51
+ | `valid/injects-field` | A `fields/` fragment is injected. |
52
+ | `valid/injects-guideline` | A `guidelines/` fragment is injected. |
53
+ | `valid/injects-mode-set` | A `mode-sets/` fragment is injected (declare-or-replace by name). |
54
+ | `valid/injects-relationship` | A `relationships/` plain-add fragment is injected. |
55
+ | `valid/override-relationship-by-uuid` | An `op: "override"` entry replaces a plain add sharing its `uuid`, regardless of file sort order. |
56
+ | `valid/plain-add-uuid-collision-append` | Two plain adds sharing a `uuid` both append rather than one overwriting the other. |
57
+ | `valid/remove-relationship-by-uuid` | An `op: "remove"` entry deletes the matching relationship. |
58
+ | `valid/tokens-sorted-order` | Multiple `tokens/*.tokens.json` files concatenate in sorted path order. |
59
+ | `valid/later-file-wins` | Two `components/` fragments declaring the same component name dedupe to one, later file wins. |
60
+ | `valid/custom-extensions-dir` | The manifest's `extensionsDir` field is honored in place of the default `extensions/` name. |
61
+ | `valid/override-entry-skips-schema-validation` | An `op: "override"` relationship entry is not schema-validated against `relationship.schema.json` (only plain adds are). |
62
+ | `valid/implementations-upsert-foundation-row` | An `implementations/` row replaces the foundation default sharing its `importPath` and is stamped with the manifest `platform`. |
63
+ | `valid/implementations-later-file-wins` | Two fragments upserting the same stamped row dedupe by `implementation` id; the later file wins. |
64
+ | `valid/implementations-remove-row` | An `op: "remove"` selector deletes the matching foundation default. |
65
+ | `valid/implementations-platform-local-component` | A fragment may target a component added by the same manifest's `components/`. |
66
+ | `valid/implementations-other-owner-untouched` | A row stamped with a different `implementation` id is never matched by another owner's upsert. |
67
+ | `invalid/override-missing-uuid` | An `op: "override"` relationship entry missing `uuid` fails loudly. |
68
+ | `invalid/unknown-term-id` | A `platform-extensions/` fragment referencing a non-existent `termId` fails loudly. |
69
+ | `invalid/component-missing-name` | A `components/` fragment missing `name` fails Layer 1 fragment schema validation. |
70
+ | `invalid/field-invalid-kind` | A `fields/` fragment with an invalid `kind` enum value fails schema validation. |
71
+ | `invalid/guideline-invalid-category` | A `guidelines/` fragment with an invalid `category` enum value fails schema validation. |
72
+ | `invalid/mode-set-missing-default` | A `mode-sets/` fragment missing the required `default` field fails Layer 1 fragment schema validation. |
73
+ | `invalid/platform-extension-missing-extends` | A `platform-extensions/` fragment missing `extends` fails schema validation. |
74
+ | `invalid/token-invalid-uuid` | A `tokens/*.tokens.json` fragment with a malformed `uuid` fails schema validation. |
75
+ | `invalid/relationship-missing-scope` | A `relationships/` plain-add fragment missing `scope` fails schema validation. |
76
+ | `invalid/extensions-dir-parent-traversal` | `extensionsDir` containing a `..` component is rejected. |
77
+ | `invalid/extensions-dir-absolute-path` | An absolute `extensionsDir` path is rejected. |
78
+ | `invalid/unknown-subdir-name` | An unrecognized subdirectory directly under `extensions/` (not one of the known categories) is rejected by name. |
79
+ | `invalid/implementations-unknown-component` | An `implementations/` fragment naming a component absent from the catalog fails loudly. |
80
+ | `invalid/implementations-remove-no-match` | An `op: "remove"` matching no owned row fails loudly. |
81
+ | `invalid/implementations-row-missing-reference` | A row with neither `package` nor `importPath` fails schema validation. |
82
+ | `invalid/implementations-unknown-op` | An `op` other than `"remove"` fails schema validation. |
@@ -2,21 +2,54 @@
2
2
  "tokens": [
3
3
  {
4
4
  "key": "btn-bg",
5
- "name": { "property": "background-color", "component": "button" },
5
+ "name": {
6
+ "property": "background-color",
7
+ "component": "button"
8
+ },
6
9
  "value": "#aaa",
7
10
  "uuid": "u-btn-bg"
8
11
  },
9
12
  {
10
13
  "key": "btn-fg",
11
- "name": { "property": "color", "component": "button" },
14
+ "name": {
15
+ "property": "color",
16
+ "component": "button"
17
+ },
12
18
  "value": "#111",
13
19
  "uuid": "u-btn-fg"
14
20
  },
15
21
  {
16
22
  "key": "chk-bg",
17
- "name": { "property": "background-color", "component": "checkbox" },
23
+ "name": {
24
+ "property": "background-color",
25
+ "component": "checkbox"
26
+ },
18
27
  "value": "#bbb",
19
28
  "uuid": "u-chk-bg"
20
29
  }
30
+ ],
31
+ "components": [
32
+ {
33
+ "$id": "https://example.com/components/button",
34
+ "name": "button",
35
+ "displayName": "Button",
36
+ "meta": {
37
+ "category": "actions",
38
+ "documentationUrl": "https://example.com"
39
+ },
40
+ "implementations": [
41
+ {
42
+ "platform": "web",
43
+ "componentName": "Button",
44
+ "package": "@react-spectrum/s2"
45
+ },
46
+ {
47
+ "platform": "web",
48
+ "componentName": "Button",
49
+ "importPath": "@adobe/spectrum-wc/components/button",
50
+ "notes": "Spectrum 2 web component custom element: swc-button."
51
+ }
52
+ ]
53
+ }
21
54
  ]
22
55
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "errors": [
3
+ {
4
+ "message_pattern": "matched no implementation row"
5
+ },
6
+ {
7
+ "message_pattern": "\"web-components\""
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "op": "remove",
6
+ "componentName": "Missing"
7
+ }
8
+ ]
9
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "errors": [
3
+ {
4
+ "message_pattern": "implementation-mapping.schema.json"
5
+ }
6
+ ]
7
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button"
7
+ }
8
+ ]
9
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "errors": [
3
+ {
4
+ "message_pattern": "\"nope\""
5
+ },
6
+ {
7
+ "message_pattern": "does not exist in the component catalog"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "component": "nope",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@adobe/spectrum-wc",
8
+ "importPath": "@adobe/spectrum-wc/components/button",
9
+ "notes": "Custom element: swc-button."
10
+ }
11
+ ]
12
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "errors": [
3
+ {
4
+ "message_pattern": "implementation-mapping.schema.json"
5
+ }
6
+ ]
7
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "op": "replace",
6
+ "componentName": "Button"
7
+ }
8
+ ]
9
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "implementations": {
3
+ "button": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2",
8
+ "notes": "second",
9
+ "implementation": "react-spectrum"
10
+ },
11
+ {
12
+ "platform": "web",
13
+ "componentName": "Button",
14
+ "importPath": "@adobe/spectrum-wc/components/button",
15
+ "notes": "Spectrum 2 web component custom element: swc-button."
16
+ }
17
+ ]
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2",
8
+ "notes": "first"
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2",
8
+ "notes": "second"
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "react-spectrum"
5
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "implementations": {
3
+ "button": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2",
8
+ "implementation": "react-spectrum",
9
+ "notes": "declared by another owner"
10
+ },
11
+ {
12
+ "platform": "web",
13
+ "componentName": "Button",
14
+ "importPath": "@adobe/spectrum-wc/components/button",
15
+ "notes": "Spectrum 2 web component custom element: swc-button."
16
+ },
17
+ {
18
+ "platform": "web",
19
+ "componentName": "Button",
20
+ "package": "@react-spectrum/s2",
21
+ "implementation": "web-components",
22
+ "notes": "web-components row with the same package"
23
+ }
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2",
8
+ "implementation": "react-spectrum",
9
+ "notes": "declared by another owner"
10
+ },
11
+ {
12
+ "platform": "web",
13
+ "componentName": "Button",
14
+ "package": "@react-spectrum/s2",
15
+ "notes": "web-components row with the same package"
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "implementations": {
3
+ "tab-bar-ios": [
4
+ {
5
+ "platform": "ios",
6
+ "componentName": "SpectrumTabBar",
7
+ "package": "SpectrumUI",
8
+ "implementation": "ios"
9
+ }
10
+ ]
11
+ }
12
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$id": "https://example.com/components/tab-bar-ios",
3
+ "name": "tab-bar-ios",
4
+ "displayName": "Tab Bar (iOS)",
5
+ "meta": {
6
+ "category": "navigation",
7
+ "documentationUrl": "https://example.com"
8
+ }
9
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "component": "tab-bar-ios",
3
+ "implementations": [
4
+ {
5
+ "platform": "ios",
6
+ "componentName": "SpectrumTabBar",
7
+ "package": "SpectrumUI"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "ios"
5
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "implementations": {
3
+ "button": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2"
8
+ }
9
+ ]
10
+ }
11
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "op": "remove",
6
+ "componentName": "Button",
7
+ "importPath": "@adobe/spectrum-wc/components/button"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "implementations": {
3
+ "button": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2"
8
+ },
9
+ {
10
+ "platform": "web",
11
+ "componentName": "Button",
12
+ "package": "@adobe/spectrum-wc",
13
+ "importPath": "@adobe/spectrum-wc/components/button",
14
+ "notes": "Custom element: swc-button.",
15
+ "implementation": "web-components"
16
+ }
17
+ ]
18
+ }
19
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@adobe/spectrum-wc",
8
+ "importPath": "@adobe/spectrum-wc/components/button",
9
+ "notes": "Custom element: swc-button."
10
+ }
11
+ ]
12
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/design-data-spec",
3
- "version": "4.3.0",
3
+ "version": "4.5.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": {
@@ -26,6 +26,7 @@
26
26
  "./schemas/manifest.schema.json": "./schemas/manifest.schema.json",
27
27
  "./schemas/value-types/color.schema.json": "./schemas/value-types/color.schema.json",
28
28
  "./schemas/component.schema.json": "./schemas/component.schema.json",
29
+ "./schemas/implementation-mapping.schema.json": "./schemas/implementation-mapping.schema.json",
29
30
  "./schemas/anatomy-part.schema.json": "./schemas/anatomy-part.schema.json",
30
31
  "./schemas/state-declaration.schema.json": "./schemas/state-declaration.schema.json",
31
32
  "./rules/rules.yaml": "./rules/rules.yaml",
@@ -42,7 +42,7 @@
42
42
  "items": {
43
43
  "$ref": "#/$defs/implementation"
44
44
  },
45
- "description": "Known platform implementations of this component. Foundation-owned canonical mappings; platform-specific overrides are not supported yet. See spec/component-format.md#implementations."
45
+ "description": "Known platform implementations of this component. Foundation data carries canonical default mappings; a platform manifest refines its own rows through extensions/implementations/. See spec/component-format.md#implementations."
46
46
  },
47
47
  "options": {
48
48
  "$ref": "#/$defs/optionsMap"
@@ -115,6 +115,11 @@
115
115
  "minLength": 1,
116
116
  "description": "Target platform identifier (for example, 'web', 'ios', or 'android'). Multiple implementations may target the same platform when they have distinct packages."
117
117
  },
118
+ "implementation": {
119
+ "type": "string",
120
+ "pattern": "^[a-z][a-z0-9-]*$",
121
+ "description": "Implementation project id from the platform-implementations registry (for example, 'react-spectrum' or 'web-components'). Distinguishes implementations that share a `platform` device target. A platform manifest stamps its own `platform` id here when merging extensions/implementations/ entries that omit it."
122
+ },
118
123
  "componentName": {
119
124
  "type": "string",
120
125
  "minLength": 1,
@@ -123,7 +128,7 @@
123
128
  "importPath": {
124
129
  "type": "string",
125
130
  "minLength": 1,
126
- "description": "Importable module or source path for a platform implementation."
131
+ "description": "Importable module or source path for a platform implementation. When `package` is also set, this SHOULD be a subpath export of that package."
127
132
  },
128
133
  "package": {
129
134
  "type": "string",
@@ -135,7 +140,7 @@
135
140
  "description": "Optional implementation-specific usage or mapping notes."
136
141
  }
137
142
  },
138
- "oneOf": [
143
+ "anyOf": [
139
144
  {
140
145
  "required": ["importPath"]
141
146
  },
@@ -0,0 +1,69 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/implementation-mapping.schema.json",
4
+ "title": "Platform implementation mapping",
5
+ "description": "Layer 1 schema for one platform manifest extensions/implementations/ fragment: the platform's own implementation rows for a single component, merged into that component's `implementations` array. See spec/manifest.md#extensionsimplementations.",
6
+ "type": "object",
7
+ "required": ["component", "implementations"],
8
+ "properties": {
9
+ "$schema": {
10
+ "type": "string",
11
+ "format": "uri"
12
+ },
13
+ "component": {
14
+ "type": "string",
15
+ "pattern": "^[a-z][a-z0-9-]*$",
16
+ "description": "Name of the component (foundation or platform-local) whose `implementations` this fragment refines."
17
+ },
18
+ "implementations": {
19
+ "type": "array",
20
+ "minItems": 1,
21
+ "items": {
22
+ "if": {
23
+ "required": ["op"]
24
+ },
25
+ "then": {
26
+ "$ref": "#/$defs/removeEntry"
27
+ },
28
+ "else": {
29
+ "$ref": "component.schema.json#/$defs/implementation"
30
+ }
31
+ },
32
+ "description": "Rows to upsert (implementation objects) or remove (`op: \"remove\"` selectors), applied in order."
33
+ }
34
+ },
35
+ "additionalProperties": false,
36
+ "$defs": {
37
+ "removeEntry": {
38
+ "type": "object",
39
+ "description": "Removes every existing row of the component whose fields equal each selector field given here.",
40
+ "required": ["op", "componentName"],
41
+ "properties": {
42
+ "op": {
43
+ "const": "remove"
44
+ },
45
+ "platform": {
46
+ "type": "string",
47
+ "minLength": 1
48
+ },
49
+ "implementation": {
50
+ "type": "string",
51
+ "pattern": "^[a-z][a-z0-9-]*$"
52
+ },
53
+ "componentName": {
54
+ "type": "string",
55
+ "minLength": 1
56
+ },
57
+ "package": {
58
+ "type": "string",
59
+ "minLength": 1
60
+ },
61
+ "importPath": {
62
+ "type": "string",
63
+ "minLength": 1
64
+ }
65
+ },
66
+ "additionalProperties": false
67
+ }
68
+ }
69
+ }
@@ -14,6 +14,21 @@
14
14
  "minLength": 1,
15
15
  "description": "Pin to a foundation release (semver or tag)."
16
16
  },
17
+ "platform": {
18
+ "type": "string",
19
+ "minLength": 1,
20
+ "description": "Machine-readable platform implementation id (e.g. \"react-spectrum\", \"web-components\", \"ios\"), matching an id in the platform-implementations registry. Optional: a manifest MAY be anonymous (e.g. a single-platform repo where identity is implicit), but SHOULD declare it once the manifest is relocatable (fetched remotely, or one of several manifests in the same repo)."
21
+ },
22
+ "platformVersion": {
23
+ "type": "string",
24
+ "minLength": 1,
25
+ "description": "Human-readable description of the platform implementation this manifest targets, e.g. \"React Spectrum 3.x\" or \"Spectrum Web Components 0.x+\". Distinct from `foundationVersion` (the pinned foundation release)."
26
+ },
27
+ "repository": {
28
+ "type": "string",
29
+ "minLength": 1,
30
+ "description": "URL of the repository that owns (or, during incubation, will eventually own) this platform manifest, e.g. \"https://github.com/adobe/react-spectrum\"."
31
+ },
17
32
  "include": {
18
33
  "type": "array",
19
34
  "items": {
@@ -138,6 +153,11 @@
138
153
  "type": "string"
139
154
  },
140
155
  "description": "Map of full term to abbreviated form, applied after ordering and before casing."
156
+ },
157
+ "prefix": {
158
+ "type": "string",
159
+ "minLength": 1,
160
+ "description": "Literal string prepended to the fully-formatted token name (after casing/delimiter are applied), unaffected by `casing` itself. For platform naming conventions that wrap the token name rather than reshape it, e.g. CSS custom properties (`--spectrum-`) or other namespaced identifier prefixes."
141
161
  }
142
162
  },
143
163
  "additionalProperties": false
@@ -20,6 +20,7 @@ The source-of-truth direction is already normative in [Evolution](evolution.md#l
20
20
 
21
21
  * Specifying the *output format* of generated artifacts. Legacy format mapping is defined in [Evolution — Legacy format contract](evolution.md#legacy-format-contract); DTCG and StyleDictionary outputs are deferred (RFC [#627](https://github.com/adobe/spectrum-design-data/discussions/627)).
22
22
  * Specifying the *consumer-facing API* of published packages (`@adobe/spectrum-tokens`, platform SDKs). Those are separately governed.
23
+ * Restating the full semantics of external implementation contracts already owned by their native standards or package ecosystems. This spec links to authoritative sources instead of duplicating their normative rules.
23
24
  * Specifying the *UI* of the authoring tools; this document defines behavioral contracts, not user-interface design.
24
25
  * Multi-user collaboration, permissions models, and Figma sync direction (open questions in RFC [#625](https://github.com/adobe/spectrum-design-data/discussions/625)).
25
26
 
@@ -6,6 +6,8 @@ This document defines the normative **component declaration** object: identity (
6
6
 
7
7
  Component declarations close the structural gap between the token name-object's `component`, `variant`, `anatomy`, and `state` fields and the declared surface of each component. Before this chapter, a token referencing `component: "button"` with `variant: "foo"` was undetectable as invalid because no machine-readable component contract existed in the same spec. After this chapter, validators enforce cross-reference rules (see [SPEC rules](#spec-rules)).
8
8
 
9
+ This chapter defines the Design Data contract for a component. It intentionally does not restate the full platform-native component API, story/example-format semantics, or other standards already owned elsewhere. When another authoritative specification already defines an implementation contract or format, this document points to that source instead of duplicating its normative rules.
10
+
9
11
  Scoped under [RFC-A — Component Contract in Design Data Spec](https://github.com/adobe/spectrum-design-data/discussions/832). See also [rfc-coordination.md](../docs/rfc-coordination.md).
10
12
 
11
13
  ## Document shape
@@ -82,34 +84,44 @@ where `{name}` matches the component's `name` field.
82
84
  ## Implementations
83
85
 
84
86
  `implementations` declares the published platform implementations corresponding
85
- to this design-system component. It is foundation-owned canonical data so
86
- consumers, including Code Connect generators, can resolve an implementation
87
- without platform-specific configuration.
87
+ to this design-system component, so consumers such as Code Connect generators
88
+ can resolve an implementation without platform-specific configuration.
89
+
90
+ Each entry **MUST** contain `platform` and `componentName`, plus at least one
91
+ implementation reference: `package` for a published package and/or
92
+ `importPath` for an importable module or source path. When both are present,
93
+ `importPath` **SHOULD** be a subpath export of `package`. `notes` is optional
94
+ context for consumers.
88
95
 
89
- Each entry **MUST** contain `platform` and `componentName`, plus exactly one
90
- implementation reference: `package` for a published package or `importPath`
91
- for an importable module/source path. `notes` is optional context for consumers.
92
- Multiple entries MAY use the same `platform` when their packages distinguish
93
- different implementations for that platform.
96
+ `platform` is a device target (`web`, `ios`, `android`), so several
97
+ implementations may share it. The optional `implementation` field names the
98
+ implementation project from the platform-implementations registry (for example
99
+ `react-spectrum` or `web-components`) to tell them apart.
94
100
 
95
101
  ```json
96
102
  "implementations": [
97
103
  {
98
104
  "platform": "web",
105
+ "implementation": "web-components",
99
106
  "componentName": "Button",
100
- "package": "@spectrum-web-components/button"
107
+ "package": "@adobe/spectrum-wc",
108
+ "importPath": "@adobe/spectrum-wc/components/button"
101
109
  }
102
110
  ]
103
111
  ```
104
112
 
105
- Platform manifests do not override `implementations` in this version. A future
106
- extension may add platform-owned overrides after consumers need them; until
107
- then, component declarations remain the canonical source of implementation
108
- names and references.
113
+ ### Layering
114
+
115
+ Foundation component data carries canonical **default** rows. A platform
116
+ manifest refines its own rows through
117
+ [`extensions/implementations/`](manifest.md#extensionsimplementations):
118
+ upserting rows it owns and removing stale ones, without restating the rest of
119
+ the component. Rows owned by another implementation are left untouched, so the
120
+ merged catalog still lists every implementation of the component.
109
121
 
110
122
  ## Options
111
123
 
112
- The `options` block declares the component's API surface — the configurable properties that affect its appearance or behavior. It mirrors the shape of `@adobe/spectrum-component-api-schemas` for backward compatibility.
124
+ The `options` block declares the component's API surface — the configurable properties that affect its appearance or behavior. It is the Design Data contract for the component surface that this repo validates; it does not duplicate the entire implementation-owned API schema. For exhaustive platform-specific semantics, follow the authoritative implementation contract instead of restating it here.
113
125
 
114
126
  **NORMATIVE:** `options` **MUST** be a JSON object. Each key is an option name; each value is an **option descriptor**.
115
127
 
@@ -424,13 +436,18 @@ A complete button component declaration:
424
436
  }
425
437
  ],
426
438
  "anatomy": [
427
- { "name": "icon", "description": "Leading icon." },
439
+ { "name": "icon", "description": "Leading icon." },
428
440
  { "name": "label", "description": "Button text.", "required": true }
429
441
  ],
430
442
  "states": [
431
- { "name": "hover", "trigger": "interaction", "precedence": 50 },
432
- { "name": "focus", "trigger": "interaction", "precedence": 60, "layered": true },
433
- { "name": "disabled", "trigger": "prop", "precedence": 100 }
443
+ { "name": "hover", "trigger": "interaction", "precedence": 50 },
444
+ {
445
+ "name": "focus",
446
+ "trigger": "interaction",
447
+ "precedence": 60,
448
+ "layered": true
449
+ },
450
+ { "name": "disabled", "trigger": "prop", "precedence": 100 }
434
451
  ],
435
452
  "lifecycle": {
436
453
  "introduced": "1.0.0-draft"
@@ -446,7 +463,10 @@ A complete button component declaration:
446
463
  {
447
464
  "name": "button",
448
465
  "displayName": "Button",
449
- "meta": { "category": "actions", "documentationUrl": "https://spectrum.adobe.com/page/button/" },
466
+ "meta": {
467
+ "category": "actions",
468
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
469
+ },
450
470
  "accessibility": {
451
471
  "role": "button",
452
472
  "intents": ["trigger"],
@@ -477,7 +497,10 @@ A complete button component declaration:
477
497
  {
478
498
  "name": "button",
479
499
  "displayName": "Button",
480
- "meta": { "category": "actions", "documentationUrl": "https://spectrum.adobe.com/page/button/" },
500
+ "meta": {
501
+ "category": "actions",
502
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
503
+ },
481
504
  "documentBlocks": [
482
505
  {
483
506
  "type": "purpose",
package/spec/index.md CHANGED
@@ -30,6 +30,8 @@ The specification defines:
30
30
  16. **Authoring workflow** — normative contract for authoring design data via tooling: authoritative source location, lifecycle operations (create / edit / deprecate / rename / alias-rewire / mode-set management), taxonomy-aware decomposition requirement, and output-generation obligations ([Authoring workflow](authoring-workflow.md)).
31
31
  17. **Relationship format** — Component/Token Relationship (CTR): the anonymous-token entity unifying `tokenBindings` and token name-object scope fields, explicit relationship context, options validation, and interim legacy-compatibility fields ([Relationship format](relationship-format.md)).
32
32
 
33
+ This specification intentionally defines the Design Data-owned contract and links to external standards for platform-native implementation semantics instead of restating those contracts as normative local rules. The repository owns taxonomy, validation, lifecycle, and schema rules; the authoritative source for native format behavior remains the external standard it describes.
34
+
33
35
  ## Conformance
34
36
 
35
37
  The key words **MUST**, **MUST NOT**, **SHOULD**, **SHOULD NOT**, **MAY**, and **OPTIONAL** in this specification are to be interpreted as described in [BCP 14](https://www.rfc-editor.org/info/bcp14) \[[RFC2119](https://www.rfc-editor.org/rfc/rfc2119.html)] \[[RFC8174](https://www.rfc-editor.org/rfc/rfc8174.html)] when, and only when, they appear in all capitals, as shown here.
package/spec/manifest.md CHANGED
@@ -8,25 +8,26 @@ 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
- | 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)) | — | — |
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
+ | Upsert / remove own implementation mappings | Yes | `extensions/implementations/` | Component `implementations` |
27
+ | Restrict allowed mode-set values | Yes | `modeSetRestrictions` | Mode sets |
28
+ | Reformat name serialization | Schema-declared only, not yet applied by the reference SDK | `formatting` | Token name strings |
29
+ | Override/remove/alias translations | No | — | — |
30
+ | Override Layer-1 schemas | No (decided; see [spike](manifest-schema-override-spike.md)) | — | — |
30
31
 
31
32
  ## Manifest document
32
33
 
@@ -41,15 +42,18 @@ A manifest **MUST** conform to [`manifest.schema.json`](../schemas/manifest.sche
41
42
 
42
43
  ## Optional fields
43
44
 
44
- | Field | Type | Description |
45
- | --------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46
- | `include` | array of string | Semantic **queries** selecting subsets of foundation tokens to materialize. |
47
- | `exclude` | array of string | Queries removing tokens from the included set. |
48
- | `overrides` | array of object | Typed overrides; each entry **MUST** preserve the target token’s **value type**. |
49
- | `extensionsDir` | string | Path (relative to the manifest) to the platform's `extensions/` directory. Default: `"extensions"`. See [`extensions/` directory](#extensions-directory) below. |
50
- | `namingExceptions` | object | Platform-local overlay on the base naming-exceptions set: names to add and/or remove for this platform's naming validation. |
51
- | `formatting` | object | Rules for serializing structured name objects into platform-specific token name strings. |
52
- | `modeSetRestrictions` | object | Mode set restrictions for this platform; see [Mode Sets — Platform restrictions](mode-sets.md#platform-restrictions). |
45
+ | Field | Type | Description |
46
+ | --------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
47
+ | `platform` | string | Machine-readable platform implementation id (e.g. `"react-spectrum"`, `"web-components"`, `"ios"`), matching an id in the platform-implementations registry. See [Identity fields](#identity-fields) below. |
48
+ | `platformVersion` | string | Human-readable description of the platform implementation, e.g. `"React Spectrum 3.x"`. Distinct from `foundationVersion`. |
49
+ | `repository` | string | URL of the repository that owns (or will eventually own) this platform manifest. |
50
+ | `include` | array of string | Semantic **queries** selecting subsets of foundation tokens to materialize. |
51
+ | `exclude` | array of string | Queries removing tokens from the included set. |
52
+ | `overrides` | array of object | Typed overrides; each entry **MUST** preserve the target token’s **value type**. |
53
+ | `extensionsDir` | string | Path (relative to the manifest) to the platform's `extensions/` directory. Default: `"extensions"`. See [`extensions/` directory](#extensions-directory) below. |
54
+ | `namingExceptions` | object | Platform-local overlay on the base naming-exceptions set: names to add and/or remove for this platform's naming validation. |
55
+ | `formatting` | object | Rules for serializing structured name objects into platform-specific token name strings. |
56
+ | `modeSetRestrictions` | object | Mode set restrictions for this platform; see [Mode Sets — Platform restrictions](mode-sets.md#platform-restrictions). |
53
57
 
54
58
  ### `include` / `exclude`
55
59
 
@@ -75,6 +79,23 @@ apply `Foundation < Platform < Product` precedence to select a single winner. To
75
79
  counts "tokens a platform ships" from `query` output should resolve first, or it will
76
80
  double-count overridden tokens.
77
81
 
82
+ ### Identity fields
83
+
84
+ `platform`, `platformVersion`, and `repository` are optional and have no effect on the
85
+ Foundation→Platform cascade — they exist so a manifest is self-describing wherever it
86
+ lives. This matters once a manifest is one of several configured in the same repo (via named
87
+ `[platforms.<id>]` profiles — see the SDK's `.design-data.toml` reference) or is fetched
88
+ from a remote repository: without `platform`, a manifest fetched by URL has no stable
89
+ identity of its own.
90
+
91
+ A single-platform repo with exactly one manifest **MAY** omit all three; the pattern
92
+ becomes worth adopting the moment a manifest is expected to move (incubating in a
93
+ monorepo ahead of a hand-off to its owning team) or coexist with siblings.
94
+
95
+ `platform`, when present, **SHOULD** match an id in the platform-implementations registry
96
+ (distinct from the token-level `platforms.json` registry, which enumerates *device
97
+ targets* like `desktop`/`mobile`, not *implementations* like `react-spectrum`).
98
+
78
99
  ### `extensions/` directory
79
100
 
80
101
  **NORMATIVE:** Platform-local additions are declared as a sibling **directory** next to
@@ -92,6 +113,7 @@ extensions/
92
113
  relationships/ <component>.json one CTR set per file
93
114
  guidelines/ <topic>.json one guideline per file
94
115
  platform-extensions/ <platform>-<registry>.json
116
+ implementations/ <component>.json one component's implementation rows
95
117
  ```
96
118
 
97
119
  **NORMATIVE:** Each subdirectory is discovered by a recursive glob for `*.json` files (for
@@ -111,6 +133,9 @@ or empty subdirectory contributes nothing and is not an error.
111
133
  injected into the corresponding catalog **add-or-replace by name** (for
112
134
  `platform-extensions/`, by `termId`; see below). When two files declare the same name,
113
135
  **the entry from the later file (in sorted path order) wins.**
136
+ * **`implementations/`** — one component's implementation rows per file, merged row-by-row into
137
+ that component's existing `implementations` array (never whole-array replaced). See
138
+ [`extensions/implementations/`](#extensionsimplementations) below.
114
139
 
115
140
  **NORMATIVE:** Each fragment file **MUST** validate against its category's real JSON schema at
116
141
  load time — this is enforced reference-SDK behavior, not an aspirational goal:
@@ -122,6 +147,8 @@ load time — this is enforced reference-SDK behavior, not an aspirational goal:
122
147
  * **`relationships/`** → `relationship.schema.json` (see the Add/Override/remove rules above)
123
148
  * **`platform-extensions/`** → `platform-extension.json` (see
124
149
  [`extensions/platform-extensions/`](#extensionsplatform-extensions) below)
150
+ * **`implementations/`** → `implementation-mapping.schema.json` (see
151
+ [`extensions/implementations/`](#extensionsimplementations) below)
125
152
  * **`tokens/`** → `cascade-file.schema.json` (see above)
126
153
 
127
154
  A fragment that fails validation against its category schema is a manifest error — the
@@ -194,6 +221,51 @@ example, platform-specific state names). **NORMATIVE:** each file **MUST** valid
194
221
  foundation registry — this mechanism annotates existing ids, it does **NOT** introduce new
195
222
  ones.
196
223
 
224
+ #### `extensions/implementations/`
225
+
226
+ Platform-owned refinements of a component's
227
+ [`implementations`](component-format.md#implementations) — the packages, import paths, and
228
+ exported names a platform ships. Foundation component data carries canonical **default** rows;
229
+ a platform manifest refines only its own rows, so the implementation team that owns the
230
+ manifest also owns its mappings, and they travel with the manifest when it is ejected.
231
+
232
+ ```json
233
+ {
234
+ "component": "button",
235
+ "implementations": [
236
+ {
237
+ "platform": "web",
238
+ "componentName": "Button",
239
+ "package": "@react-spectrum/s2"
240
+ },
241
+ {
242
+ "op": "remove",
243
+ "componentName": "Button",
244
+ "package": "@react-spectrum/button"
245
+ }
246
+ ]
247
+ }
248
+ ```
249
+
250
+ **NORMATIVE:** each file **MUST** validate against `implementation-mapping.schema.json`.
251
+ `component` **MUST** name a component in the catalog after `extensions/components/` has been
252
+ applied (so a fragment may target a platform-local component); an unknown component is a
253
+ manifest error. Entries apply in array order, files in sorted path order.
254
+
255
+ **NORMATIVE:** each entry has an *owner*: its own `implementation` field if present, otherwise
256
+ the manifest's top-level `platform` id. A manifest only matches rows its owner owns — rows
257
+ stamped with the same `implementation` id, or unstamped foundation defaults. Rows stamped with
258
+ a different `implementation` id are never modified or removed.
259
+
260
+ * **Upsert** (no `op`) — a full implementation row. When the entry omits `implementation` and
261
+ the manifest declares `platform`, the stored row is stamped with that id. The row replaces the
262
+ first existing row with the same `platform` and `componentName` that either carries the same
263
+ `implementation` id, or is an unstamped foundation default sharing a `package` or `importPath`
264
+ value. Otherwise it is appended.
265
+ * **Remove** (`"op": "remove"`) — a selector of `componentName` plus any of `platform`,
266
+ `implementation`, `package`, `importPath`. It deletes every owned row whose values equal every
267
+ selector field given. A remove that matches no owned row is a manifest error.
268
+
197
269
  ### `namingExceptions`
198
270
 
199
271
  Platform-local overlay on the base naming-exceptions set used by naming validation.
@@ -216,6 +288,7 @@ A platform **MAY** declare formatting rules that control how structured name obj
216
288
  | `casing` | string | One of: `kebab-case`, `camelCase`, `PascalCase`, `SCREAMING_SNAKE_CASE`. Default: `kebab-case`. |
217
289
  | `delimiter` | string | Character(s) separating concepts in the serialized string (e.g. `-`, `_`, `.`, `/`). Default: `-`. |
218
290
  | `abbreviations` | object | Map of full term → abbreviated form (e.g. `{ "background": "bg" }`). Abbreviations are applied after concept ordering and before casing. |
291
+ | `prefix` | string | Literal string prepended to the fully-formatted token name, applied after `casing`/`delimiter` and unaffected by either — for platform naming conventions that wrap the token name rather than reshape it (e.g. CSS custom properties like `--spectrum-`). |
219
292
 
220
293
  **NORMATIVE:** When `formatting` is absent, the default serialization defined in [Taxonomy](taxonomy.md#default-serialization-legacy-format) is used.
221
294
 
package/spec/taxonomy.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  This document defines the **token taxonomy**: a hierarchical system of concept categories that classify design tokens, the **token term vocabulary** of allowed words within each category, and the **formatting style** rules that control serialization of structured token names into platform-consumable strings.
6
6
 
7
+ This chapter defines the Design Data-owned taxonomy contract. It intentionally does not restate platform-native naming semantics or external standards already owned elsewhere; when a platform or implementation standard defines those behaviors, this spec links to that authority instead of copying its normative rules.
8
+
7
9
  ## Motivation
8
10
 
9
11
  "Naming convention" is too broad a term when discussing malleability across platform teams. A token name like `accent-background-color-hover` embeds multiple independent decisions: