@adobe/design-data-spec 0.14.0 → 1.0.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 (89) hide show
  1. package/components/accordion.json +27 -4
  2. package/components/action-button.json +27 -4
  3. package/components/action-group.json +51 -10
  4. package/components/alert-banner.json +15 -2
  5. package/components/alert-dialog.json +18 -8
  6. package/components/avatar-group.json +24 -2
  7. package/components/avatar.json +72 -13
  8. package/components/badge.json +120 -31
  9. package/components/body.json +24 -2
  10. package/components/bottom-navigation-android.json +18 -4
  11. package/components/breadcrumbs.json +34 -7
  12. package/components/button-group.json +32 -5
  13. package/components/button.json +50 -10
  14. package/components/calendar.json +9 -2
  15. package/components/cards.json +21 -9
  16. package/components/checkbox-group.json +27 -6
  17. package/components/checkbox.json +15 -2
  18. package/components/close-button.json +24 -4
  19. package/components/coach-indicator.json +40 -14
  20. package/components/code.json +18 -2
  21. package/components/color-handle.json +24 -10
  22. package/components/color-slider.json +33 -12
  23. package/components/combo-box.json +48 -8
  24. package/components/contextual-help.json +78 -27
  25. package/components/date-picker.json +12 -2
  26. package/components/detail.json +24 -4
  27. package/components/divider.json +21 -4
  28. package/components/drop-zone.json +12 -2
  29. package/components/field-label.json +33 -6
  30. package/components/heading.json +36 -4
  31. package/components/help-text.json +25 -5
  32. package/components/illustrated-message.json +21 -4
  33. package/components/in-field-progress-button.json +29 -3
  34. package/components/in-field-progress-circle.json +23 -3
  35. package/components/in-line-alert.json +33 -11
  36. package/components/link.json +18 -4
  37. package/components/list-view.json +12 -2
  38. package/components/menu.json +42 -5
  39. package/components/meter.json +30 -4
  40. package/components/number-field.json +32 -5
  41. package/components/opacity-checkerboard.json +25 -2
  42. package/components/picker.json +41 -7
  43. package/components/popover.json +69 -25
  44. package/components/progress-bar.json +39 -8
  45. package/components/progress-circle.json +21 -4
  46. package/components/radio-group.json +42 -8
  47. package/components/scroll-zoom-bar.json +21 -4
  48. package/components/search-field.json +15 -2
  49. package/components/segmented-control.json +27 -6
  50. package/components/select-box.json +9 -2
  51. package/components/side-navigation.json +12 -2
  52. package/components/slider.json +17 -3
  53. package/components/standard-dialog.json +12 -2
  54. package/components/standard-panel.json +33 -6
  55. package/components/status-light.json +87 -28
  56. package/components/steplist.json +9 -2
  57. package/components/swatch-group.json +48 -8
  58. package/components/swatch.json +39 -9
  59. package/components/switch.json +15 -2
  60. package/components/tab-bar-ios.json +18 -4
  61. package/components/table.json +24 -4
  62. package/components/tabs.json +9 -2
  63. package/components/tag-field.json +27 -4
  64. package/components/tag-group.json +21 -4
  65. package/components/takeover-dialog.json +9 -2
  66. package/components/text-area.json +50 -7
  67. package/components/text-field.json +32 -5
  68. package/components/thumbnail.json +38 -1
  69. package/components/title.json +24 -2
  70. package/components/toast.json +15 -2
  71. package/components/tooltip.json +27 -4
  72. package/components/tree-view.json +42 -8
  73. package/conformance/invalid/SPEC-019/dataset.json +14 -1
  74. package/conformance/invalid/SPEC-037/dataset.json +86 -0
  75. package/conformance/invalid/SPEC-037/expected-errors.json +20 -0
  76. package/conformance/invalid/SPEC-038/dataset.json +20 -0
  77. package/conformance/invalid/SPEC-038/expected-errors.json +10 -0
  78. package/conformance/valid/SPEC-037/dataset.json +119 -0
  79. package/conformance/valid/SPEC-038/dataset.json +29 -0
  80. package/conformance/valid/component-refs/dataset.json +41 -7
  81. package/package.json +1 -1
  82. package/rules/rules.yaml +29 -1
  83. package/schemas/anatomy-part.schema.json +4 -0
  84. package/schemas/component.schema.json +33 -3
  85. package/schemas/state-declaration.schema.json +4 -0
  86. package/spec/agent-surface.md +1 -1
  87. package/spec/anatomy-format.md +14 -0
  88. package/spec/component-format.md +84 -28
  89. package/spec/state-model.md +18 -4
package/rules/rules.yaml CHANGED
@@ -168,7 +168,7 @@ rules:
168
168
  name: component-variant-valid
169
169
  severity: error
170
170
  category: reference-integrity
171
- assert: Token name-object 'variant' field value MUST match a value in the declared 'variant' option enum for the referenced component, when that enum exists.
171
+ assert: Token name-object 'variant' field value MUST match a value in the declared 'variant' option `values` list for the referenced component, when that list exists.
172
172
  message: "Token '{token}' has variant '{variant}' which is not declared on component '{component}'"
173
173
  spec_ref: spec/component-format.md#options
174
174
  introduced_in: "1.0.0-draft"
@@ -355,3 +355,31 @@ rules:
355
355
  message: "Token '{token}' references deprecated component '{component}' (deprecated since {version}); update the reference or mark the token deprecated"
356
356
  spec_ref: spec/component-format.md#lifecycle
357
357
  introduced_in: "1.0.0-draft"
358
+
359
+ - id: SPEC-037
360
+ name: sub-entity-deprecation-cascade
361
+ severity: warning
362
+ category: reference-integrity
363
+ assert: >
364
+ Tokens SHOULD NOT reference a deprecated anatomy part, deprecated component state, or
365
+ deprecated option value via their `name` object unless the token is itself deprecated.
366
+ Non-deprecated tokens referencing deprecated sub-entities are surfaced as warnings to
367
+ prompt authors to update the reference or mark the token deprecated.
368
+ Deprecation is declared via `lifecycle.deprecated` on anatomy parts, states, and individual
369
+ option values within the `values` array on option descriptors.
370
+ message: "Token '{token}' references deprecated {kind} '{value}' on component '{component}' (deprecated since {version}); update the reference or mark the token deprecated"
371
+ spec_ref: spec/component-format.md#lifecycle
372
+ introduced_in: "1.0.0-draft"
373
+
374
+ - id: SPEC-038
375
+ name: option-enum-obsolete
376
+ severity: warning
377
+ category: component-contract
378
+ assert: >
379
+ Option descriptors SHOULD NOT use the JSON Schema `enum` keyword. The `enum` field is
380
+ silently accepted at Layer 1 (optionDescriptor uses additionalProperties: true) but has
381
+ no effect on SDK rules or lifecycle cascade. Use `values` instead so per-value lifecycle
382
+ metadata can be attached to individual values.
383
+ message: "Component '{component}' option '{option}' uses the obsolete `enum` keyword; replace with a `values` array so per-value lifecycle metadata can be expressed"
384
+ spec_ref: spec/component-format.md#option-descriptor
385
+ introduced_in: "1.0.0-draft"
@@ -35,6 +35,10 @@
35
35
  "items": { "$ref": "document-block.schema.json" },
36
36
  "minItems": 1,
37
37
  "description": "Typed prose blocks for this anatomy part. See spec/document-blocks.md (Phase 9)."
38
+ },
39
+ "lifecycle": {
40
+ "$ref": "component.schema.json#/$defs/lifecycle",
41
+ "description": "Version lifecycle metadata for this anatomy part. Set lifecycle.deprecated to signal that tokens referencing this part should migrate."
38
42
  }
39
43
  },
40
44
  "additionalProperties": false
@@ -108,7 +108,8 @@
108
108
  },
109
109
  "optionDescriptor": {
110
110
  "type": "object",
111
- "description": "JSON Schema-compatible descriptor for a single component option. Standard JSON Schema keywords (type, enum, default, description, pattern, minimum, maximum, items, properties, etc.) are all permitted.",
111
+ "description": "Descriptor for a single component option. The `type` keyword follows JSON Schema conventions. Use `values` (not `enum`) to declare permitted values — the structured array allows per-value lifecycle metadata. Other JSON Schema keywords (default, description, pattern, minimum, maximum, items, properties, $ref) are permitted as needed.",
112
+ "$comment": "additionalProperties: true is required to allow JSON Schema passthrough keywords (pattern, minimum, etc.). This means the legacy `enum` keyword is silently accepted at Layer 1; SPEC-038 closes the gap at Layer 2 by warning when `enum` is present.",
112
113
  "properties": {
113
114
  "type": {
114
115
  "oneOf": [
@@ -142,10 +143,12 @@
142
143
  }
143
144
  ]
144
145
  },
145
- "enum": {
146
+ "values": {
146
147
  "type": "array",
147
148
  "minItems": 1,
148
- "description": "Exhaustive list of permitted values."
149
+ "uniqueItems": true,
150
+ "items": { "$ref": "#/$defs/optionValue" },
151
+ "description": "Exhaustive list of permitted values for this option, each with optional per-value metadata. Use this instead of the JSON Schema `enum` keyword so that lifecycle annotations can be attached to individual values. Consumed by SPEC-019 (variant validation) and SPEC-037 (deprecation cascade)."
149
152
  },
150
153
  "default": {
151
154
  "description": "Default value when the option is not specified."
@@ -162,6 +165,25 @@
162
165
  },
163
166
  "additionalProperties": true
164
167
  },
168
+ "optionValue": {
169
+ "type": "object",
170
+ "required": ["value"],
171
+ "description": "A single permitted value for a component option, with optional per-value metadata.",
172
+ "properties": {
173
+ "value": {
174
+ "description": "The permitted option value. Typically a string; the schema does not constrain the type so booleans and numbers remain expressible."
175
+ },
176
+ "description": {
177
+ "type": "string",
178
+ "description": "Plain-text description of what this value means."
179
+ },
180
+ "lifecycle": {
181
+ "$ref": "#/$defs/lifecycle",
182
+ "description": "Version lifecycle metadata for this value. Set lifecycle.deprecated to signal that tokens referencing this value should migrate (SPEC-037)."
183
+ }
184
+ },
185
+ "additionalProperties": false
186
+ },
165
187
  "slotDeclaration": {
166
188
  "type": "object",
167
189
  "required": ["name"],
@@ -209,6 +231,10 @@
209
231
  "type": "array",
210
232
  "items": { "$ref": "document-block.schema.json" },
211
233
  "description": "Typed prose blocks for this anatomy part. See spec/document-blocks.md (Phase 9)."
234
+ },
235
+ "lifecycle": {
236
+ "$ref": "#/$defs/lifecycle",
237
+ "description": "Version lifecycle metadata for this anatomy part. Set lifecycle.deprecated to signal that tokens referencing this part should migrate."
212
238
  }
213
239
  },
214
240
  "additionalProperties": false
@@ -255,6 +281,10 @@
255
281
  "type": "boolean",
256
282
  "default": false,
257
283
  "description": "Whether this state prevents all user interaction. See spec/accessibility.md."
284
+ },
285
+ "lifecycle": {
286
+ "$ref": "#/$defs/lifecycle",
287
+ "description": "Version lifecycle metadata for this state. Set lifecycle.deprecated to signal that tokens referencing this state should migrate."
258
288
  }
259
289
  },
260
290
  "additionalProperties": false
@@ -30,6 +30,10 @@
30
30
  "type": "boolean",
31
31
  "default": false,
32
32
  "description": "When true, this state composes on top of the winning non-layered state rather than competing with it. Use for focus rings, selection overlays, and similar compositing states."
33
+ },
34
+ "lifecycle": {
35
+ "$ref": "component.schema.json#/$defs/lifecycle",
36
+ "description": "Version lifecycle metadata for this state. Set lifecycle.deprecated to signal that tokens referencing this state should migrate."
33
37
  }
34
38
  },
35
39
  "additionalProperties": false
@@ -96,7 +96,7 @@ The `describe_component` tool returns the component declaration object as stored
96
96
  "displayName": "Button",
97
97
  "meta": { "category": "actions", "documentationUrl": "https://spectrum.adobe.com/page/button/" },
98
98
  "options": {
99
- "variant": { "type": "string", "enum": ["accent", "negative", "primary", "secondary"], "default": "accent" }
99
+ "variant": { "type": "string", "values": [{"value": "accent"}, {"value": "negative"}, {"value": "primary"}, {"value": "secondary"}], "default": "accent" }
100
100
  },
101
101
  "anatomy": [
102
102
  { "name": "icon", "description": "Leading icon." },
@@ -20,6 +20,7 @@ An anatomy part is a **JSON object** that appears as an element of a component d
20
20
  | `description` | string | OPTIONAL | Plain-text description of the part's visual role and boundaries. |
21
21
  | `required` | boolean | OPTIONAL | Whether this part is always rendered regardless of configuration. Default: `false`. |
22
22
  | `contains` | array of strings | OPTIONAL | Informative list of child anatomy part names nested within this part (e.g. a `field` contains `["label", "help-text"]`). |
23
+ | `lifecycle` | object | OPTIONAL | Version lifecycle metadata for this anatomy part — see [lifecycle](#lifecycle). |
23
24
 
24
25
  **NORMATIVE:** No properties beyond those listed above are permitted in an anatomy part object. Additional fields **MUST** cause a Layer 1 schema error.
25
26
 
@@ -51,6 +52,18 @@ When `required` is `true`, the anatomy part is unconditionally rendered (e.g. a
51
52
 
52
53
  Each string in `contains` **MUST** match the pattern `^[a-z][a-z0-9-]*$`. References to anatomy part names not declared on the same component are permitted (they may refer to sub-component anatomy in layered designs) but validators **MAY** surface a warning for unresolved references.
53
54
 
55
+ ### `lifecycle`
56
+
57
+ **OPTIONAL.** A version lifecycle object tracking the history of this anatomy part. Uses the same shape as the component-level `lifecycle` block (see [Component format — Lifecycle](component-format.md#lifecycle)).
58
+
59
+ | Field | Type | Description |
60
+ | ------------------- | ------ | ---------------------------------------------------------------------------------------------- |
61
+ | `deprecated` | string | Spec version when this anatomy part was deprecated. A truthy string signals deprecation. |
62
+ | `deprecatedComment` | string | Human-readable explanation of the deprecation and migration path (e.g. `"Renamed to thumb."`). |
63
+ | `replacedBy` | string | `name` of the replacement anatomy part. |
64
+
65
+ **ADVISORY:** When an anatomy part carries a `lifecycle.deprecated` value, non-deprecated tokens that reference this anatomy part via `name.anatomy` **SHOULD** be updated to remove or replace the reference. Rule SPEC-037 fires an advisory warning for such references to prompt migration.
66
+
54
67
  ```json
55
68
  "anatomy": [
56
69
  {
@@ -121,6 +134,7 @@ The following rules in the Layer 2 rule catalog (`rules/rules.yaml`) apply to an
121
134
  | SPEC-024 | `anatomy-part-name-unique` | error | Anatomy part `name` values **MUST** be unique within a single component's `anatomy` array. |
122
135
  | SPEC-025 | `anatomy-requires-component` | error | A token name object **MUST NOT** include an `anatomy` field unless a `component` field is also present. |
123
136
  | SPEC-035 | `anatomy-part-name-registry-sync` | warning | A component anatomy part's `name` **SHOULD** appear in the canonical anatomy-terms registry (`anatomy-terms.json`). |
137
+ | SPEC-037 | `sub-entity-deprecation-cascade` | warning | A non-deprecated token **SHOULD NOT** reference a deprecated anatomy part via `name.anatomy`. Advisory warning prompts migration. |
124
138
 
125
139
  ## Full example
126
140
 
@@ -88,31 +88,72 @@ The `options` block declares the component's API surface — the configurable pr
88
88
 
89
89
  An option descriptor is a JSON object with the following fields:
90
90
 
91
- | Field | Type | Required | Description |
92
- | ------------- | --------------- | -------- | -------------------------------------------------------------------------------- |
93
- | `type` | string or array | OPTIONAL | JSON Schema primitive type(s): `"string"`, `"boolean"`, `"number"`, `"integer"`. |
94
- | `enum` | array | OPTIONAL | Exhaustive list of permitted values. |
95
- | `default` | any | OPTIONAL | Default value when the option is not specified. |
96
- | `description` | string | OPTIONAL | Plain-text description of what the option controls. |
97
- | `$ref` | URI string | OPTIONAL | Reference to a shared type schema (e.g. `workflow-icon.json`). |
91
+ | Field | Type | Required | Description |
92
+ | ------------- | --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
93
+ | `type` | string or array | OPTIONAL | JSON Schema primitive type(s): `"string"`, `"boolean"`, `"number"`, `"integer"`. |
94
+ | `values` | array | OPTIONAL | Exhaustive list of permitted values, each an [`optionValue`](#optionvalue) object. Use this instead of JSON Schema's `enum` keyword so per-value lifecycle metadata can be expressed without a separate sidecar map. |
95
+ | `default` | any | OPTIONAL | Default value when the option is not specified. |
96
+ | `description` | string | OPTIONAL | Plain-text description of what the option controls. |
97
+ | `$ref` | URI string | OPTIONAL | Reference to a shared type schema (e.g. `workflow-icon.json`). |
98
+
99
+ ### optionValue
100
+
101
+ Each entry in `values` is an object:
102
+
103
+ | Field | Type | Required | Description |
104
+ | ------------- | ------ | -------- | ---------------------------------------------------------------------------------------- |
105
+ | `value` | any | REQUIRED | The permitted option value. |
106
+ | `description` | string | OPTIONAL | Plain-text description of what this value means. |
107
+ | `lifecycle` | object | OPTIONAL | Version lifecycle metadata. Set `lifecycle.deprecated` to signal migration via SPEC-037. |
98
108
 
99
109
  **NORMATIVE:** Each key in `options` **MUST** be camelCase.
100
110
 
101
111
  **NORMATIVE:** Boolean option names **MUST** begin with `is` or `has` (e.g. `isDisabled`, `hasIcon`).
102
112
 
103
- **NORMATIVE:** When `enum` is present, token name-object `variant` field values referencing this component **MUST** be drawn from the declared `variant` option enum (rule SPEC-019). Other option enums are informative for tooling but do not currently drive SPEC rules.
113
+ **NORMATIVE:** When `values` is present, token name-object `variant` field values referencing this component **MUST** be drawn from the declared `variant` option `values` list (rule SPEC-019). Other option `values` lists are informative for tooling but do not currently drive SPEC rules.
114
+
115
+ **ADVISORY:** When a value in `values` carries a `lifecycle.deprecated` string and a non-deprecated token references that value via its `name` object field, SPEC-037 fires an advisory warning prompting migration or token deprecation.
116
+
117
+ Example with a deprecated option value:
118
+
119
+ ```json
120
+ "variant": {
121
+ "type": "string",
122
+ "values": [
123
+ { "value": "primary" },
124
+ { "value": "secondary" },
125
+ {
126
+ "value": "cta",
127
+ "lifecycle": {
128
+ "deprecated": "1.0.0-draft",
129
+ "deprecatedComment": "Use primary instead."
130
+ }
131
+ }
132
+ ]
133
+ }
134
+ ```
104
135
 
105
136
  ```json
106
137
  "options": {
107
138
  "variant": {
108
139
  "type": "string",
109
- "enum": ["accent", "negative", "primary", "secondary"],
140
+ "values": [
141
+ { "value": "accent" },
142
+ { "value": "negative" },
143
+ { "value": "primary" },
144
+ { "value": "secondary" }
145
+ ],
110
146
  "default": "accent",
111
147
  "description": "Visual emphasis level."
112
148
  },
113
149
  "size": {
114
150
  "type": "string",
115
- "enum": ["s", "m", "l", "xl"],
151
+ "values": [
152
+ { "value": "s" },
153
+ { "value": "m" },
154
+ { "value": "l" },
155
+ { "value": "xl" }
156
+ ],
116
157
  "default": "m"
117
158
  },
118
159
  "isDisabled": {
@@ -186,12 +227,13 @@ The `anatomy` block declares the component's named **visual parts** — the anat
186
227
 
187
228
  Each anatomy part carries at minimum:
188
229
 
189
- | Field | Type | Required | Description |
190
- | ------------- | ---------------- | -------- | -------------------------------------------------------------- |
191
- | `name` | string | REQUIRED | Anatomy part identifier (e.g. `icon`, `label`, `handle`). |
192
- | `description` | string | OPTIONAL | Plain-text description of the part. |
193
- | `required` | boolean | OPTIONAL | Whether this part is always present. Default: `false`. |
194
- | `contains` | array of strings | OPTIONAL | Informative: other anatomy part names nested within this part. |
230
+ | Field | Type | Required | Description |
231
+ | ------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
232
+ | `name` | string | REQUIRED | Anatomy part identifier (e.g. `icon`, `label`, `handle`). |
233
+ | `description` | string | OPTIONAL | Plain-text description of the part. |
234
+ | `required` | boolean | OPTIONAL | Whether this part is always present. Default: `false`. |
235
+ | `contains` | array of strings | OPTIONAL | Informative: other anatomy part names nested within this part. |
236
+ | `lifecycle` | object | OPTIONAL | Version lifecycle metadata for this part. When `lifecycle.deprecated` is set, SPEC-037 fires on referencing tokens. |
195
237
 
196
238
  See [`spec/anatomy-format.md`](anatomy-format.md) for constraints, cross-field validation, and the full anatomy part schema.
197
239
 
@@ -216,6 +258,7 @@ Each state carries at minimum:
216
258
  | `trigger` | string | OPTIONAL | `"prop"` for persistent prop-driven states (e.g. `isDisabled`) or `"interaction"` for runtime interaction states (hover, focus, pressed). |
217
259
  | `precedence` | integer | OPTIONAL | Resolution precedence; higher integer wins when multiple states are active. |
218
260
  | `layered` | boolean | OPTIONAL | `true` for states that compose with others (e.g. focus ring over hover). Default: `false`. |
261
+ | `lifecycle` | object | OPTIONAL | Version lifecycle metadata for this state. When `lifecycle.deprecated` is set, SPEC-037 fires on referencing tokens. |
219
262
 
220
263
  See [`spec/state-model.md`](state-model.md) for the full state resolution algorithm, trigger semantics, and precedence rules.
221
264
 
@@ -271,14 +314,17 @@ The `context` field is informative. It is used by `describe_component` (Phase 8
271
314
 
272
315
  The following rules are added to the Layer 2 rule catalog (`rules/rules.yaml`) by this chapter. New component cross-reference rules start at SPEC-018 to avoid collision with existing token rules (SPEC-001–SPEC-017).
273
316
 
274
- | Rule ID | Name | Severity | Assert |
275
- | -------- | ---------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
276
- | SPEC-018 | `component-name-exists` | error | Token `component` field value **MUST** match the `name` of a declared component in the dataset. |
277
- | SPEC-019 | `component-variant-valid` | error | Token `variant` field value **MUST** match a value in the declared `variant` option enum for the referenced component (when that enum exists). |
278
- | SPEC-020 | `component-anatomy-valid` | error | Token `anatomy` field value **MUST** match the `name` of a declared anatomy part on the referenced component. |
279
- | SPEC-021 | `component-slot-vocabulary` | warning | Component `slots` entries with a `name` outside the canonical vocabulary **SHOULD** include a `description`. Custom slot names without descriptions are surfaced as warnings. |
280
- | SPEC-022 | `component-state-valid` | error | Token `state` field value **MUST** match the `name` of a declared state on the referenced component (when state declarations are present). |
281
- | SPEC-027 | `token-binding-token-exists` | error | Each `tokenBindings[].token` value **MUST** match the name of a declared token in the dataset (Phase 6.7). |
317
+ | Rule ID | Name | Severity | Assert |
318
+ | -------- | -------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
319
+ | SPEC-018 | `component-name-exists` | error | Token `component` field value **MUST** match the `name` of a declared component in the dataset. |
320
+ | SPEC-019 | `component-variant-valid` | error | Token `variant` field value **MUST** match a value in the declared `variant` option `values` list for the referenced component (when that list exists). |
321
+ | SPEC-020 | `component-anatomy-valid` | error | Token `anatomy` field value **MUST** match the `name` of a declared anatomy part on the referenced component. |
322
+ | SPEC-021 | `component-slot-vocabulary` | warning | Component `slots` entries with a `name` outside the canonical vocabulary **SHOULD** include a `description`. Custom slot names without descriptions are surfaced as warnings. |
323
+ | SPEC-022 | `component-state-valid` | error | Token `state` field value **MUST** match the `name` of a declared state on the referenced component (when state declarations are present). |
324
+ | SPEC-027 | `token-binding-token-exists` | error | Each `tokenBindings[].token` value **MUST** match the name of a declared token in the dataset (Phase 6.7). |
325
+ | SPEC-036 | `component-deprecation-cascade` | warning | A non-deprecated token **SHOULD NOT** reference a deprecated component via `name.component`. Advisory warning prompts updating the component reference or marking the token deprecated. |
326
+ | SPEC-037 | `sub-entity-deprecation-cascade` | warning | A non-deprecated token **SHOULD NOT** reference a deprecated anatomy part, state, or option value via `name.*`. Advisory warning prompts migration. Requires `lifecycle` on anatomy/state or `lifecycle` on the matching `values` entry on the option descriptor. |
327
+ | SPEC-038 | `option-enum-obsolete` | warning | An option descriptor **SHOULD NOT** use the JSON Schema `enum` keyword. `additionalProperties: true` silently accepts `enum` at Layer 1; SPEC-038 flags it at Layer 2 so authors replace it with the `values` array. |
282
328
 
283
329
  ## Full example
284
330
 
@@ -299,18 +345,28 @@ A complete button component declaration:
299
345
  "options": {
300
346
  "variant": {
301
347
  "type": "string",
302
- "enum": ["accent", "negative", "primary", "secondary"],
348
+ "values": [
349
+ { "value": "accent" },
350
+ { "value": "negative" },
351
+ { "value": "primary" },
352
+ { "value": "secondary" }
353
+ ],
303
354
  "default": "accent",
304
355
  "description": "Visual emphasis level."
305
356
  },
306
357
  "style": {
307
358
  "type": "string",
308
- "enum": ["fill", "outline"],
359
+ "values": [{ "value": "fill" }, { "value": "outline" }],
309
360
  "default": "fill"
310
361
  },
311
362
  "size": {
312
363
  "type": "string",
313
- "enum": ["s", "m", "l", "xl"],
364
+ "values": [
365
+ { "value": "s" },
366
+ { "value": "m" },
367
+ { "value": "l" },
368
+ { "value": "xl" }
369
+ ],
314
370
  "default": "m"
315
371
  },
316
372
  "isDisabled": { "type": "boolean", "default": false },
@@ -322,7 +378,7 @@ A complete button component declaration:
322
378
  },
323
379
  "staticColor": {
324
380
  "type": "string",
325
- "enum": ["white", "black"],
381
+ "values": [{ "value": "white" }, { "value": "black" }],
326
382
  "description": "Static color for use on colored backgrounds. Must not be set for the default variant."
327
383
  }
328
384
  },
@@ -34,6 +34,7 @@ A state declaration is a **JSON object** that appears as an element of a compone
34
34
  | `trigger` | string | OPTIONAL | `"prop"` for persistent prop-driven states; `"interaction"` for runtime interaction states. See [Trigger semantics](#trigger-semantics). |
35
35
  | `precedence` | integer | OPTIONAL | Resolution precedence; higher value wins when multiple non-layered states are active simultaneously. Defaults to `0` if omitted. |
36
36
  | `layered` | boolean | OPTIONAL | When `true`, this state composes on top of the winning non-layered state rather than competing with it. Default: `false`. |
37
+ | `lifecycle` | object | OPTIONAL | Version lifecycle metadata for this state — see [lifecycle](#lifecycle). |
37
38
 
38
39
  **NORMATIVE:** No properties beyond those listed above are permitted in a state declaration object. Additional fields **MUST** cause a Layer 1 schema error.
39
40
 
@@ -76,6 +77,18 @@ When `layered: true`, the state does not participate in the non-layered preceden
76
77
 
77
78
  Typical use: focus ring states (`focus`, `focus-visible`) that must be visible regardless of whether the component is also in a `hover` or `selected` state.
78
79
 
80
+ ### `lifecycle`
81
+
82
+ **OPTIONAL.** A version lifecycle object tracking the history of this state declaration. Uses the same shape as the component-level `lifecycle` block (see [Component format — Lifecycle](component-format.md#lifecycle)).
83
+
84
+ | Field | Type | Description |
85
+ | ------------------- | ------ | ------------------------------------------------------------------------------------------------ |
86
+ | `deprecated` | string | Spec version when this state was deprecated. A truthy string signals deprecation. |
87
+ | `deprecatedComment` | string | Human-readable explanation of the deprecation and migration path (e.g. `"Use active instead."`). |
88
+ | `replacedBy` | string | `name` of the replacement state. |
89
+
90
+ **ADVISORY:** When a state carries a `lifecycle.deprecated` value, non-deprecated tokens that reference this state via `name.state` **SHOULD** be updated to remove or replace the reference. Rule SPEC-037 fires an advisory warning for such references to prompt migration.
91
+
79
92
  ## Trigger semantics
80
93
 
81
94
  ### `prop` trigger
@@ -185,10 +198,11 @@ See [Token format — Name object](token-format.md#name-object) for the full nam
185
198
 
186
199
  The following rules in the Layer 2 rule catalog (`rules/rules.yaml`) apply to state declarations. SPEC-022 was introduced in Phase 6.1 (component-format); SPEC-026 is introduced by this chapter.
187
200
 
188
- | Rule ID | Name | Severity | Assert |
189
- | -------- | ------------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
190
- | SPEC-022 | `component-state-valid` | error | Token `state` field value **MUST** match the `name` of a declared state on the referenced component (when state declarations are present). |
191
- | SPEC-026 | `state-custom-name-documented` | warning | State declarations with a `name` outside the canonical state vocabulary **SHOULD** include a `description` field documenting the state's semantics. |
201
+ | Rule ID | Name | Severity | Assert |
202
+ | -------- | -------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
203
+ | SPEC-022 | `component-state-valid` | error | Token `state` field value **MUST** match the `name` of a declared state on the referenced component (when state declarations are present). |
204
+ | SPEC-026 | `state-custom-name-documented` | warning | State declarations with a `name` outside the canonical state vocabulary **SHOULD** include a `description` field documenting the state's semantics. |
205
+ | SPEC-037 | `sub-entity-deprecation-cascade` | warning | A non-deprecated token **SHOULD NOT** reference a deprecated state via `name.state`. Advisory warning prompts migration. |
192
206
 
193
207
  ## Full example
194
208