@adobe/design-data-spec 0.9.0 → 0.10.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 (179) hide show
  1. package/components/accordion.json +18 -1
  2. package/components/action-bar.json +12 -1
  3. package/components/action-button.json +19 -1
  4. package/components/action-group.json +12 -1
  5. package/components/alert-banner.json +11 -1
  6. package/components/alert-dialog.json +19 -1
  7. package/components/avatar-group.json +4 -1
  8. package/components/avatar.json +4 -1
  9. package/components/badge.json +4 -1
  10. package/components/body.json +3 -0
  11. package/components/bottom-navigation-android.json +17 -0
  12. package/components/breadcrumbs.json +19 -2
  13. package/components/button-group.json +12 -1
  14. package/components/button.json +21 -3
  15. package/components/calendar.json +22 -0
  16. package/components/cards.json +18 -1
  17. package/components/checkbox-group.json +17 -1
  18. package/components/checkbox.json +24 -1
  19. package/components/close-button.json +19 -1
  20. package/components/coach-indicator.json +4 -1
  21. package/components/coach-mark.json +13 -1
  22. package/components/code.json +3 -0
  23. package/components/color-area.json +24 -1
  24. package/components/color-handle.json +13 -1
  25. package/components/color-loupe.json +4 -1
  26. package/components/color-slider.json +24 -1
  27. package/components/color-wheel.json +24 -1
  28. package/components/combo-box.json +24 -1
  29. package/components/contextual-help.json +18 -1
  30. package/components/date-picker.json +23 -1
  31. package/components/detail.json +3 -0
  32. package/components/divider.json +4 -1
  33. package/components/drop-zone.json +18 -1
  34. package/components/field-label.json +4 -1
  35. package/components/heading.json +3 -0
  36. package/components/help-text.json +5 -2
  37. package/components/illustrated-message.json +4 -1
  38. package/components/in-field-progress-button.json +18 -0
  39. package/components/in-field-progress-circle.json +4 -1
  40. package/components/in-line-alert.json +11 -1
  41. package/components/link.json +19 -1
  42. package/components/list-view.json +18 -1
  43. package/components/menu.json +19 -1
  44. package/components/meter.json +4 -1
  45. package/components/number-field.json +24 -1
  46. package/components/opacity-checkerboard.json +4 -1
  47. package/components/picker.json +24 -1
  48. package/components/popover.json +12 -1
  49. package/components/progress-bar.json +4 -1
  50. package/components/progress-circle.json +4 -1
  51. package/components/radio-button.json +24 -1
  52. package/components/radio-group.json +17 -1
  53. package/components/rating.json +24 -1
  54. package/components/scroll-zoom-bar.json +12 -0
  55. package/components/search-field.json +28 -1
  56. package/components/segmented-control.json +18 -1
  57. package/components/select-box.json +18 -1
  58. package/components/side-navigation.json +18 -1
  59. package/components/slider.json +24 -1
  60. package/components/standard-dialog.json +19 -1
  61. package/components/standard-panel.json +3 -0
  62. package/components/status-light.json +4 -1
  63. package/components/steplist.json +18 -1
  64. package/components/swatch-group.json +12 -1
  65. package/components/swatch.json +18 -1
  66. package/components/switch.json +19 -1
  67. package/components/tab-bar-ios.json +17 -0
  68. package/components/table.json +23 -1
  69. package/components/tabs.json +18 -1
  70. package/components/tag-field.json +18 -1
  71. package/components/tag-group.json +18 -1
  72. package/components/tag.json +18 -1
  73. package/components/takeover-dialog.json +19 -1
  74. package/components/text-area.json +28 -1
  75. package/components/text-field.json +28 -1
  76. package/components/thumbnail.json +4 -1
  77. package/components/title.json +40 -10
  78. package/components/toast.json +13 -1
  79. package/components/tooltip.json +17 -1
  80. package/components/tray.json +11 -0
  81. package/components/tree-view.json +19 -1
  82. package/conformance/README.md +7 -7
  83. package/conformance/invalid/SPEC-001/dataset.json +10 -0
  84. package/conformance/invalid/SPEC-001/expected-errors.json +1 -1
  85. package/conformance/invalid/SPEC-002/dataset.json +16 -0
  86. package/conformance/invalid/SPEC-002/expected-errors.json +1 -1
  87. package/conformance/invalid/SPEC-003/dataset.json +15 -0
  88. package/conformance/invalid/SPEC-003/expected-errors.json +1 -1
  89. package/conformance/invalid/SPEC-004/dataset.json +15 -0
  90. package/conformance/invalid/SPEC-004/expected-errors.json +1 -1
  91. package/conformance/invalid/SPEC-005/dataset.json +11 -0
  92. package/conformance/invalid/SPEC-005/expected-errors.json +1 -1
  93. package/conformance/invalid/SPEC-006/dataset.json +15 -0
  94. package/conformance/invalid/SPEC-006/expected-errors.json +1 -1
  95. package/conformance/invalid/SPEC-007/dataset.json +9 -0
  96. package/conformance/invalid/SPEC-007/expected-errors.json +10 -0
  97. package/conformance/invalid/SPEC-008/dataset.json +25 -0
  98. package/conformance/invalid/SPEC-008/expected-errors.json +2 -1
  99. package/conformance/invalid/SPEC-009/dataset.json +12 -0
  100. package/conformance/invalid/SPEC-009/expected-errors.json +10 -0
  101. package/conformance/invalid/SPEC-010/dataset.json +12 -0
  102. package/conformance/invalid/SPEC-011/dataset.json +25 -0
  103. package/conformance/invalid/SPEC-012/dataset.json +16 -0
  104. package/conformance/invalid/SPEC-013/dataset.json +11 -0
  105. package/conformance/invalid/SPEC-014/dataset.json +12 -0
  106. package/conformance/invalid/SPEC-014/expected-errors.json +1 -1
  107. package/conformance/invalid/SPEC-015/dataset.json +22 -0
  108. package/conformance/invalid/SPEC-015/expected-errors.json +10 -0
  109. package/conformance/invalid/SPEC-015/tokens.tokens.json +19 -0
  110. package/conformance/invalid/SPEC-016/dataset.json +11 -0
  111. package/conformance/invalid/SPEC-016/expected-errors.json +2 -2
  112. package/conformance/invalid/SPEC-017/dataset.json +10 -0
  113. package/conformance/invalid/SPEC-024/dataset.json +4 -3
  114. package/conformance/invalid/SPEC-024/expected-errors.json +2 -2
  115. package/conformance/invalid/SPEC-025/dataset.json +12 -0
  116. package/conformance/invalid/SPEC-025/expected-errors.json +10 -0
  117. package/conformance/invalid/SPEC-026/dataset.json +18 -0
  118. package/conformance/invalid/SPEC-026/expected-errors.json +10 -0
  119. package/conformance/invalid/SPEC-028/dataset.json +21 -0
  120. package/conformance/invalid/SPEC-028/expected-errors.json +10 -0
  121. package/conformance/invalid/SPEC-029/dataset.json +20 -0
  122. package/conformance/invalid/SPEC-029/expected-errors.json +10 -0
  123. package/conformance/invalid/SPEC-030/dataset.json +15 -0
  124. package/conformance/invalid/SPEC-030/expected-errors.json +10 -0
  125. package/conformance/invalid/SPEC-031/dataset.json +17 -0
  126. package/conformance/invalid/SPEC-031/expected-errors.json +10 -0
  127. package/conformance/invalid/SPEC-032/dataset.json +27 -0
  128. package/conformance/invalid/SPEC-032/expected-errors.json +15 -0
  129. package/conformance/resolution/base-fallback/expected.json +1 -1
  130. package/conformance/resolution/product-layer-wins/expected.json +5 -0
  131. package/conformance/resolution/product-layer-wins/input/tokens.tokens.json +7 -0
  132. package/conformance/resolution/product-layer-wins/product-context.json +11 -0
  133. package/conformance/resolution/product-layer-wins/query.json +4 -0
  134. package/conformance/resolution/specificity-wins/expected.json +1 -1
  135. package/conformance/valid/SPEC-014/dataset.json +12 -0
  136. package/conformance/valid/SPEC-016/dataset.json +16 -0
  137. package/conformance/valid/SPEC-025/dataset.json +24 -0
  138. package/conformance/valid/SPEC-026/dataset.json +15 -0
  139. package/conformance/valid/SPEC-028/dataset.json +21 -0
  140. package/conformance/valid/SPEC-029/dataset.json +24 -0
  141. package/conformance/valid/SPEC-030/dataset.json +17 -0
  142. package/conformance/valid/SPEC-031/dataset.json +24 -0
  143. package/conformance/valid/SPEC-032/dataset.json +16 -0
  144. package/conformance/valid/component-refs/README.md +5 -0
  145. package/conformance/valid/component-with-accessibility.json +32 -0
  146. package/fields/color-scheme.json +2 -2
  147. package/fields/contrast.json +2 -2
  148. package/fields/density.json +1 -1
  149. package/fields/scale.json +2 -2
  150. package/fields/size.json +1 -1
  151. package/package.json +4 -13
  152. package/rules/rules.yaml +40 -4
  153. package/schemas/accessibility.schema.json +60 -0
  154. package/schemas/component.schema.json +19 -0
  155. package/schemas/field.schema.json +2 -2
  156. package/schemas/manifest.schema.json +1 -1
  157. package/schemas/{dimension.schema.json → mode-set.schema.json} +3 -3
  158. package/schemas/token.schema.json +5 -5
  159. package/schemas/value-types/README.md +20 -0
  160. package/schemas/value-types/drop-shadow.schema.json +5 -5
  161. package/schemas/value-types/typography-scale.schema.json +2 -2
  162. package/schemas/value-types/typography.schema.json +5 -5
  163. package/spec/accessibility-adapters.md +219 -0
  164. package/spec/agent-surface.md +4 -4
  165. package/spec/cascade.md +7 -7
  166. package/spec/component-format.md +32 -0
  167. package/spec/document-blocks.md +1 -1
  168. package/spec/index.md +22 -20
  169. package/spec/manifest.md +2 -2
  170. package/spec/mode-sets.md +64 -0
  171. package/spec/query.md +18 -18
  172. package/spec/taxonomy.md +5 -5
  173. package/spec/token-format.md +12 -12
  174. package/spec/dimensions.md +0 -64
  175. package/src/canonical.js +0 -61
  176. package/src/validate.js +0 -190
  177. /package/conformance/resolution/alias-resolved-after-cascade/{dimensions → mode-sets}/color-scheme.json +0 -0
  178. /package/conformance/resolution/base-fallback/{dimensions → mode-sets}/color-scheme.json +0 -0
  179. /package/conformance/resolution/specificity-wins/{dimensions → mode-sets}/color-scheme.json +0 -0
@@ -35,7 +35,7 @@ A token's `name` field **MAY** be a non-empty plain string instead of a name obj
35
35
 
36
36
  **NORMATIVE:** String-named tokens **MUST** trigger rule SPEC-017 (severity: `warning`, category: `tech-debt`). The warning surfaces the token as tracked debt requiring future remediation.
37
37
 
38
- **NORMATIVE:** String-named tokens **MUST NOT** participate in name-object cascade dimension matching, specificity calculation, or registry vocabulary checks (SPEC-009 does not apply).
38
+ **NORMATIVE:** String-named tokens **MUST NOT** participate in name-object cascade mode-set matching, specificity calculation, or registry vocabulary checks (SPEC-009 does not apply).
39
39
 
40
40
  **RECOMMENDED:** Authors **SHOULD** treat string names as a temporary escape hatch and track a remediation plan. Each string-named token **SHOULD** eventually be given a structured name object, at which point SPEC-017 no longer fires.
41
41
 
@@ -45,9 +45,9 @@ The **name object** identifies the token in a structured way. Implementations us
45
45
 
46
46
  **NORMATIVE fields** (all string unless noted):
47
47
 
48
- The set of available name-object fields is declared in the design system's **field catalog** (`fields/` directory). Each field declaration conforms to [`field.schema.json`](../schemas/field.schema.json) and specifies its kind (`semantic` or `dimension`), vocabulary registry, validation severity, and default serialization position. See [Taxonomy](taxonomy.md) for the full concept category hierarchy, component anatomy vs. token objects, and serialization rules.
48
+ The set of available name-object fields is declared in the design system's **field catalog** (`fields/` directory). Each field declaration conforms to [`field.schema.json`](../schemas/field.schema.json) and specifies its kind (`semantic` or `mode-set`), vocabulary registry, validation severity, and default serialization position. See [Taxonomy](taxonomy.md) for the full concept category hierarchy, component anatomy vs. token objects, and serialization rules.
49
49
 
50
- Fields are divided into **semantic fields** (identity, structure, intent) and **dimension fields** (axes of variation for cascade resolution). The tables below list Spectrum's foundation-standard fields as declared in the catalog.
50
+ Fields are divided into **semantic fields** (identity, structure, intent) and **mode-set fields** (axes of variation for cascade resolution). The tables below list Spectrum's foundation-standard fields as declared in the catalog.
51
51
 
52
52
  #### Semantic fields
53
53
 
@@ -67,16 +67,16 @@ Fields are divided into **semantic fields** (identity, structure, intent) and **
67
67
  | `density` | OPTIONAL | Density | Space within or around component parts (e.g. `spacious`, `compact`). |
68
68
  | `shape` | OPTIONAL | Shape | Relative to overall component shape (e.g. `uniform`). |
69
69
 
70
- #### Dimension fields
70
+ #### Mode-set fields
71
71
 
72
- | Field | Status | Description |
73
- | --------------- | -------- | ----------------------------------------------------------------------------------------------- |
74
- | `colorScheme` | OPTIONAL | Dimension: light / dark / wireframe / etc. |
75
- | `scale` | OPTIONAL | Dimension: platform density scale (e.g. `desktop`, `mobile`). Distinct from semantic `size`. |
76
- | `contrast` | OPTIONAL | Dimension: contrast level (e.g. `regular`, `high`). |
77
- | Additional keys | OPTIONAL | Other dimensions declared in the dataset’s dimension catalog (see [Dimensions](dimensions.md)). |
72
+ | Field | Status | Description |
73
+ | --------------- | -------- | ------------------------------------------------------------------------------------------- |
74
+ | `colorScheme` | OPTIONAL | Mode set: light / dark / wireframe / etc. |
75
+ | `scale` | OPTIONAL | Mode set: platform density scale (e.g. `desktop`, `mobile`). Distinct from semantic `size`. |
76
+ | `contrast` | OPTIONAL | Mode set: contrast level (e.g. `regular`, `high`). |
77
+ | Additional keys | OPTIONAL | Other mode sets declared in the dataset’s mode set catalog (see [Mode Sets](mode-sets.md)). |
78
78
 
79
- **NORMATIVE:** Each field is validated according to the `validation` severity declared in its field declaration. Semantic fields typically use **advisory** severity (warning); dimension fields use **strict** severity (error). See [Taxonomy — Name object field categories](taxonomy.md#name-object-field-categories).
79
+ **NORMATIVE:** Each field is validated according to the `validation` severity declared in its field declaration. Semantic fields typically use **advisory** severity (warning); mode-set fields use **strict** severity (error). See [Taxonomy — Name object field categories](taxonomy.md#name-object-field-categories).
80
80
 
81
81
  **RECOMMENDED:** Name objects use a consistent key ordering in authored files for diffs; this is not a conformance requirement. Concept ordering for serialized names is defined in [Taxonomy — Default serialization](taxonomy.md#default-serialization-legacy-format).
82
82
 
@@ -249,7 +249,7 @@ The current `@adobe/spectrum-tokens` JSON uses **sets** (`color-set`, `scale-set
249
249
  | ------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
250
250
  | Identity field | `id` | `uuid` |
251
251
  | Name model | `name.original` (string) + `name.structure` (nested object) | Flat fields directly on `name` (`property`, `component`, `colorScheme`, …) |
252
- | Complexity tracking | `name.semanticComplexity` (stored on token) | Computed at validation time from dimension declarations |
252
+ | Complexity tracking | `name.semanticComplexity` (stored on token) | Computed at validation time from mode set declarations |
253
253
 
254
254
  **NORMATIVE:** The flat `name` object defined in this spec is the authoritative serialization format. RFC [#646](https://github.com/adobe/spectrum-design-data/issues/646)'s `name.structure` / `name.original` shape is not a conformance target; it remains a useful reference for the analytical model that informed this design.
255
255
 
@@ -1,64 +0,0 @@
1
- # Dimensions
2
-
3
- **Spec version:** `1.0.0-draft` (see [Overview](index.md))
4
-
5
- This document defines how **dimensions** (modes such as color scheme, scale, contrast) are **declared**, assigned **defaults**, and validated for **coverage**.
6
-
7
- ## Dimension declaration
8
-
9
- A **dimension declaration** is a JSON object describing one axis of variation. It **MUST** conform to [`dimension.schema.json`](../schemas/dimension.schema.json) (canonical `$id`: `https://opensource.adobe.com/spectrum-design-data/schemas/v0/dimension.schema.json`).
10
-
11
- ### Required fields
12
-
13
- | Field | Description |
14
- | --------- | --------------------------------------------------------- |
15
- | `name` | Stable identifier for the dimension (e.g. `colorScheme`). |
16
- | `modes` | Array of allowed mode values (strings). |
17
- | `default` | Default mode; **MUST** be a member of `modes`. |
18
-
19
- ### Optional fields
20
-
21
- | Field | Description |
22
- | ------------- | ------------------------------------ |
23
- | `description` | Human-readable documentation. |
24
- | `coverage` | Rules for mode coverage (see below). |
25
-
26
- ## Built-in dimensions
27
-
28
- These dimensions are declared in the `dimensions/` catalog (see [Dimension catalog](#dimension-catalog)) and **SHOULD** be used consistently across Spectrum-compatible datasets:
29
-
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.
43
-
44
- ## Optional dimensions
45
-
46
- Additional dimensions (e.g. `language`, `motion`) **MAY** be declared in a dataset’s dimension catalog. Token name objects **MAY** include keys matching declared dimension names.
47
-
48
- ## Defaults and specificity
49
-
50
- **NORMATIVE:** A token name object **omitting** a dimension field implies the token applies under the dimension’s **`default`** mode for specificity and matching purposes unless the spec for that dimension states otherwise.
51
-
52
- **NORMATIVE:** Only **non-default** dimension fields on the name object increase **semantic specificity** (see [Cascade](cascade.md)).
53
-
54
- ## Coverage validation
55
-
56
- **RECOMMENDED:** If a dimension’s `coverage` requires **peer modes** (e.g. defining `dark` requires `light`), validators implement rule **`SPEC-005`** (see `rules/rules.yaml`).
57
-
58
- **RECOMMENDED:** Explicit **combination** tokens are used for rare cross-dimensional cases instead of inferring Cartesian products.
59
-
60
- ## References
61
-
62
- * [#646 — Token Schema Structure and Validation System](https://github.com/adobe/spectrum-design-data/discussions/646)
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)
package/src/canonical.js DELETED
@@ -1,61 +0,0 @@
1
- /*
2
- Copyright 2026 Adobe. All rights reserved.
3
- This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License. You may obtain a copy
5
- of the License at http://www.apache.org/licenses/LICENSE-2.0
6
-
7
- Unless required by applicable law or agreed to in writing, software distributed under
8
- the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
- OF ANY KIND, either express or implied. See the License for the specific language
10
- governing permissions and limitations under the License.
11
- */
12
-
13
- // Canonical vocabulary sets derived from spec chapters.
14
- // Sources: spec/component-format.md, spec/anatomy-format.md, spec/state-model.md
15
-
16
- export const CANONICAL_SLOTS = new Set([
17
- "default",
18
- "icon",
19
- "label",
20
- "help-text",
21
- "negative-help-text",
22
- "action",
23
- "heading",
24
- "description",
25
- "hero",
26
- "footer",
27
- "tooltip",
28
- ]);
29
-
30
- export const CANONICAL_ANATOMY_PARTS = new Set([
31
- "body",
32
- "checkmark",
33
- "disclosure-triangle",
34
- "field",
35
- "handle",
36
- "header",
37
- "icon",
38
- "label",
39
- "picker",
40
- "progress-bar",
41
- "swatch",
42
- "thumbnail",
43
- "track",
44
- "value",
45
- ]);
46
-
47
- export const CANONICAL_STATES = new Set([
48
- "default",
49
- "hover",
50
- "focus",
51
- "focus-visible",
52
- "active",
53
- "pressed",
54
- "selected",
55
- "indeterminate",
56
- "disabled",
57
- "read-only",
58
- "invalid",
59
- "valid",
60
- "dragging",
61
- ]);
package/src/validate.js DELETED
@@ -1,190 +0,0 @@
1
- /*
2
- Copyright 2026 Adobe. All rights reserved.
3
- This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License. You may obtain a copy
5
- of the License at http://www.apache.org/licenses/LICENSE-2.0
6
-
7
- Unless required by applicable law or agreed to in writing, software distributed under
8
- the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
- OF ANY KIND, either express or implied. See the License for the specific language
10
- governing permissions and limitations under the License.
11
- */
12
-
13
- /**
14
- * Layer 2 cross-reference validator for design-data-spec.
15
- *
16
- * Implements SPEC-018 through SPEC-027: semantic rules that validate token
17
- * name-object fields against component declarations, validate component
18
- * declarations internally, and validate tokenBindings references.
19
- *
20
- * @see spec/component-format.md#spec-rules
21
- * @see spec/anatomy-format.md#spec-rules
22
- * @see spec/state-model.md#spec-rules
23
- */
24
-
25
- import {
26
- CANONICAL_SLOTS,
27
- CANONICAL_ANATOMY_PARTS,
28
- CANONICAL_STATES,
29
- } from "./canonical.js";
30
-
31
- /**
32
- * @typedef {{ ruleId: string, severity: 'error'|'warning', message: string, tokenName?: string, componentName?: string }} Diagnostic
33
- * @typedef {{ name: string|object, [key: string]: unknown }} Token
34
- * @typedef {{ name: string, options?: object, anatomy?: Array<{name:string,description?:string}>, slots?: Array<{name:string,description?:string}>, states?: Array<{name:string,trigger?:string,precedence?:number,layered?:boolean,description?:string}> }} ComponentDeclaration
35
- * @typedef {{ tokens?: Token[], components?: ComponentDeclaration[] }} Dataset
36
- */
37
-
38
- /**
39
- * Validate a dataset for SPEC-018 through SPEC-024 compliance.
40
- *
41
- * @param {Dataset} dataset
42
- * @returns {Diagnostic[]}
43
- */
44
- export function validateDataset(dataset) {
45
- const tokens = dataset.tokens ?? [];
46
- const components = dataset.components ?? [];
47
-
48
- // Build component lookup map keyed by name.
49
- const componentMap = new Map(components.map((c) => [c.name, c]));
50
-
51
- const diagnostics = [];
52
-
53
- // --- Token cross-reference rules ---
54
- for (const token of tokens) {
55
- const name = token.name;
56
- // String names (SPEC-017 escape hatch) skip cross-reference checks.
57
- if (typeof name !== "object" || name === null) continue;
58
-
59
- const tokenLabel = JSON.stringify(name);
60
-
61
- if (name.component != null) {
62
- // SPEC-018: component name must be declared
63
- if (!componentMap.has(name.component)) {
64
- diagnostics.push({
65
- ruleId: "SPEC-018",
66
- severity: "error",
67
- message: `Token '${tokenLabel}' references undeclared component '${name.component}'`,
68
- tokenName: tokenLabel,
69
- });
70
- // Can't validate further fields without a component declaration.
71
- continue;
72
- }
73
-
74
- const component = componentMap.get(name.component);
75
-
76
- // SPEC-019: variant must be in component's variant option enum
77
- if (name.variant != null) {
78
- const variantEnum = component.options?.variant?.enum;
79
- if (Array.isArray(variantEnum) && !variantEnum.includes(name.variant)) {
80
- diagnostics.push({
81
- ruleId: "SPEC-019",
82
- severity: "error",
83
- message: `Token '${tokenLabel}' has variant '${name.variant}' which is not declared on component '${name.component}'`,
84
- tokenName: tokenLabel,
85
- componentName: name.component,
86
- });
87
- }
88
- }
89
-
90
- // SPEC-020: anatomy must match a declared anatomy part name
91
- if (name.anatomy != null) {
92
- const declaredParts = new Set(
93
- (component.anatomy ?? []).map((p) => p.name),
94
- );
95
- if (declaredParts.size > 0 && !declaredParts.has(name.anatomy)) {
96
- diagnostics.push({
97
- ruleId: "SPEC-020",
98
- severity: "error",
99
- message: `Token '${tokenLabel}' references undeclared anatomy part '${name.anatomy}' on component '${name.component}'`,
100
- tokenName: tokenLabel,
101
- componentName: name.component,
102
- });
103
- }
104
- }
105
-
106
- // SPEC-022: state must match a declared state name (only when states are declared)
107
- if (name.state != null) {
108
- const declaredStates = new Set(
109
- (component.states ?? []).map((s) => s.name),
110
- );
111
- if (declaredStates.size > 0 && !declaredStates.has(name.state)) {
112
- diagnostics.push({
113
- ruleId: "SPEC-022",
114
- severity: "error",
115
- message: `Token '${tokenLabel}' references undeclared state '${name.state}' on component '${name.component}'`,
116
- tokenName: tokenLabel,
117
- componentName: name.component,
118
- });
119
- }
120
- }
121
- }
122
- }
123
-
124
- // --- Component declaration internal rules ---
125
- for (const component of components) {
126
- const cName = component.name;
127
-
128
- // SPEC-021: custom slot names should have descriptions
129
- for (const slot of component.slots ?? []) {
130
- if (!CANONICAL_SLOTS.has(slot.name) && !slot.description) {
131
- diagnostics.push({
132
- ruleId: "SPEC-021",
133
- severity: "warning",
134
- message: `Component '${cName}' has custom slot '${slot.name}' with no description — add a description or use a canonical slot name`,
135
- componentName: cName,
136
- });
137
- }
138
- }
139
-
140
- // SPEC-023: custom anatomy part names should have descriptions
141
- for (const part of component.anatomy ?? []) {
142
- if (!CANONICAL_ANATOMY_PARTS.has(part.name) && !part.description) {
143
- diagnostics.push({
144
- ruleId: "SPEC-023",
145
- severity: "warning",
146
- message: `Component '${cName}' has custom anatomy part '${part.name}' with no description`,
147
- componentName: cName,
148
- });
149
- }
150
- }
151
-
152
- // SPEC-024: custom state names should have descriptions
153
- for (const state of component.states ?? []) {
154
- if (!CANONICAL_STATES.has(state.name) && !state.description) {
155
- diagnostics.push({
156
- ruleId: "SPEC-024",
157
- severity: "warning",
158
- message: `Component '${cName}' has custom state '${state.name}' with no description`,
159
- componentName: cName,
160
- });
161
- }
162
- }
163
- }
164
-
165
- // --- Token binding rules ---
166
-
167
- // SPEC-027: each tokenBindings[].token must resolve to a known token name.
168
- // String-named tokens are matched directly. Name-object tokens are skipped
169
- // here because tokenBindings always reference tokens by their string name.
170
- const tokenNameSet = new Set(
171
- tokens
172
- .map((t) => (typeof t.name === "string" ? t.name : null))
173
- .filter(Boolean),
174
- );
175
-
176
- for (const component of components) {
177
- for (const binding of component.tokenBindings ?? []) {
178
- if (!tokenNameSet.has(binding.token)) {
179
- diagnostics.push({
180
- ruleId: "SPEC-027",
181
- severity: "error",
182
- message: `Component '${component.name}' tokenBindings references unknown token '${binding.token}'`,
183
- componentName: component.name,
184
- });
185
- }
186
- }
187
- }
188
-
189
- return diagnostics;
190
- }