@adobe/design-data-spec 0.13.0 → 0.15.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.
@@ -0,0 +1,26 @@
1
+ {
2
+ "tokens": [
3
+ {
4
+ "name": {
5
+ "component": "old-widget",
6
+ "property": "color"
7
+ },
8
+ "value": "#000000"
9
+ }
10
+ ],
11
+ "components": [
12
+ {
13
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/old-widget.json",
14
+ "name": "old-widget",
15
+ "displayName": "Old Widget",
16
+ "meta": {
17
+ "category": "actions",
18
+ "documentationUrl": "https://spectrum.adobe.com/page/old-widget/"
19
+ },
20
+ "lifecycle": {
21
+ "deprecated": "1.0.0-draft",
22
+ "deprecatedComment": "Superseded by new-widget."
23
+ }
24
+ }
25
+ ]
26
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-036",
6
+ "severity": "warning",
7
+ "message_pattern": ".*deprecated component.*"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "tokens": [
3
+ {
4
+ "name": {
5
+ "component": "slider",
6
+ "anatomy": "handle",
7
+ "property": "background-color"
8
+ },
9
+ "value": "#0265dc"
10
+ },
11
+ {
12
+ "name": {
13
+ "component": "button",
14
+ "state": "pressed",
15
+ "property": "background-color"
16
+ },
17
+ "value": "#cccccc"
18
+ },
19
+ {
20
+ "name": {
21
+ "component": "button",
22
+ "variant": "cta",
23
+ "property": "background-color"
24
+ },
25
+ "value": "#0265dc"
26
+ }
27
+ ],
28
+ "components": [
29
+ {
30
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/slider.json",
31
+ "name": "slider",
32
+ "displayName": "Slider",
33
+ "meta": {
34
+ "category": "forms",
35
+ "documentationUrl": "https://spectrum.adobe.com/page/slider/"
36
+ },
37
+ "anatomy": [
38
+ {
39
+ "name": "handle",
40
+ "lifecycle": {
41
+ "deprecated": "1.0.0-draft",
42
+ "deprecatedComment": "Renamed to thumb."
43
+ }
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/button.json",
49
+ "name": "button",
50
+ "displayName": "Button",
51
+ "meta": {
52
+ "category": "actions",
53
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
54
+ },
55
+ "states": [
56
+ {
57
+ "name": "pressed",
58
+ "lifecycle": {
59
+ "deprecated": "1.0.0-draft",
60
+ "deprecatedComment": "Use active instead."
61
+ }
62
+ }
63
+ ],
64
+ "options": {
65
+ "variant": {
66
+ "type": "string",
67
+ "enum": ["primary", "secondary", "cta"],
68
+ "deprecatedEnumValues": {
69
+ "cta": {
70
+ "deprecated": "1.0.0-draft",
71
+ "deprecatedComment": "Use primary instead."
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ ]
78
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-037",
6
+ "severity": "warning",
7
+ "message_pattern": ".*deprecated anatomy part.*"
8
+ },
9
+ {
10
+ "rule_id": "SPEC-037",
11
+ "severity": "warning",
12
+ "message_pattern": ".*deprecated state.*"
13
+ },
14
+ {
15
+ "rule_id": "SPEC-037",
16
+ "severity": "warning",
17
+ "message_pattern": ".*deprecated option value.*"
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "tokens": [
3
+ {
4
+ "name": {
5
+ "component": "new-widget",
6
+ "property": "background-color"
7
+ },
8
+ "value": "#0265dc"
9
+ },
10
+ {
11
+ "name": {
12
+ "component": "old-widget",
13
+ "property": "color"
14
+ },
15
+ "value": "#000000",
16
+ "deprecated": "1.0.0-draft"
17
+ }
18
+ ],
19
+ "components": [
20
+ {
21
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/new-widget.json",
22
+ "name": "new-widget",
23
+ "displayName": "New Widget",
24
+ "meta": {
25
+ "category": "actions",
26
+ "documentationUrl": "https://spectrum.adobe.com/page/new-widget/"
27
+ }
28
+ },
29
+ {
30
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/old-widget.json",
31
+ "name": "old-widget",
32
+ "displayName": "Old Widget",
33
+ "meta": {
34
+ "category": "actions",
35
+ "documentationUrl": "https://spectrum.adobe.com/page/old-widget/"
36
+ },
37
+ "lifecycle": {
38
+ "deprecated": "1.0.0-draft",
39
+ "deprecatedComment": "Superseded by new-widget."
40
+ }
41
+ }
42
+ ]
43
+ }
@@ -0,0 +1,107 @@
1
+ {
2
+ "tokens": [
3
+ {
4
+ "name": {
5
+ "component": "slider",
6
+ "anatomy": "track",
7
+ "property": "background-color"
8
+ },
9
+ "value": "#e0e0e0"
10
+ },
11
+ {
12
+ "name": {
13
+ "component": "slider",
14
+ "anatomy": "handle",
15
+ "property": "background-color"
16
+ },
17
+ "value": "#0265dc",
18
+ "deprecated": "1.0.0-draft"
19
+ },
20
+ {
21
+ "name": {
22
+ "component": "button",
23
+ "state": "hover",
24
+ "property": "background-color"
25
+ },
26
+ "value": "#eeeeee"
27
+ },
28
+ {
29
+ "name": {
30
+ "component": "button",
31
+ "state": "pressed",
32
+ "property": "background-color"
33
+ },
34
+ "value": "#cccccc",
35
+ "deprecated": "1.0.0-draft"
36
+ },
37
+ {
38
+ "name": {
39
+ "component": "button",
40
+ "variant": "primary",
41
+ "property": "background-color"
42
+ },
43
+ "value": "#0265dc"
44
+ },
45
+ {
46
+ "name": {
47
+ "component": "button",
48
+ "variant": "cta",
49
+ "property": "background-color"
50
+ },
51
+ "value": "#0265dc",
52
+ "deprecated": "1.0.0-draft"
53
+ }
54
+ ],
55
+ "components": [
56
+ {
57
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/slider.json",
58
+ "name": "slider",
59
+ "displayName": "Slider",
60
+ "meta": {
61
+ "category": "forms",
62
+ "documentationUrl": "https://spectrum.adobe.com/page/slider/"
63
+ },
64
+ "anatomy": [
65
+ { "name": "track" },
66
+ {
67
+ "name": "handle",
68
+ "lifecycle": {
69
+ "deprecated": "1.0.0-draft",
70
+ "deprecatedComment": "Renamed to thumb."
71
+ }
72
+ }
73
+ ]
74
+ },
75
+ {
76
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/button.json",
77
+ "name": "button",
78
+ "displayName": "Button",
79
+ "meta": {
80
+ "category": "actions",
81
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
82
+ },
83
+ "states": [
84
+ { "name": "hover" },
85
+ {
86
+ "name": "pressed",
87
+ "lifecycle": {
88
+ "deprecated": "1.0.0-draft",
89
+ "deprecatedComment": "Use active instead."
90
+ }
91
+ }
92
+ ],
93
+ "options": {
94
+ "variant": {
95
+ "type": "string",
96
+ "enum": ["primary", "secondary", "cta"],
97
+ "deprecatedEnumValues": {
98
+ "cta": {
99
+ "deprecated": "1.0.0-draft",
100
+ "deprecatedComment": "Use primary instead."
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ ]
107
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/design-data-spec",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "Design Data Specification — prose, JSON Schemas, rule catalog, and conformance fixtures for Spectrum design data",
5
5
  "type": "module",
6
6
  "repository": {
package/rules/rules.yaml CHANGED
@@ -343,3 +343,30 @@ rules:
343
343
  message: "Component '{entity}' anatomy part name '{value}' is not in the design-system-registry anatomy-terms vocabulary"
344
344
  spec_ref: spec/anatomy-format.md#canonical-anatomy-vocabulary
345
345
  introduced_in: "1.0.0-draft"
346
+
347
+ - id: SPEC-036
348
+ name: component-deprecation-cascade
349
+ severity: warning
350
+ category: reference-integrity
351
+ assert: >
352
+ Tokens SHOULD NOT reference a deprecated component via `name.component` unless the token
353
+ is itself deprecated. Non-deprecated tokens referencing a deprecated component are surfaced
354
+ as warnings to prompt authors to update the reference or mark the token deprecated.
355
+ message: "Token '{token}' references deprecated component '{component}' (deprecated since {version}); update the reference or mark the token deprecated"
356
+ spec_ref: spec/component-format.md#lifecycle
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-enum 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
+ Requires `lifecycle.deprecated` on anatomy parts and states, and `deprecatedEnumValues`
369
+ on option descriptors (introduced in this version of the schema).
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"
@@ -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
@@ -158,6 +158,14 @@
158
158
  "type": "string",
159
159
  "format": "uri-reference",
160
160
  "description": "Reference to a shared type schema."
161
+ },
162
+ "deprecatedEnumValues": {
163
+ "type": "object",
164
+ "description": "Per-enum-value lifecycle metadata keyed by enum value string. Values absent from this map are not deprecated. Consumed by SPEC-037.",
165
+ "$comment": "Keys are not validated against sibling 'enum' — JSON Schema cannot cross-reference sibling values without unevaluatedProperties. Entries for values absent from 'enum' are silently ignored by validators. A future SPEC rule may close this gap.",
166
+ "additionalProperties": {
167
+ "$ref": "#/$defs/lifecycle"
168
+ }
161
169
  }
162
170
  },
163
171
  "additionalProperties": true
@@ -209,6 +217,10 @@
209
217
  "type": "array",
210
218
  "items": { "$ref": "document-block.schema.json" },
211
219
  "description": "Typed prose blocks for this anatomy part. See spec/document-blocks.md (Phase 9)."
220
+ },
221
+ "lifecycle": {
222
+ "$ref": "#/$defs/lifecycle",
223
+ "description": "Version lifecycle metadata for this anatomy part. Set lifecycle.deprecated to signal that tokens referencing this part should migrate."
212
224
  }
213
225
  },
214
226
  "additionalProperties": false
@@ -255,6 +267,10 @@
255
267
  "type": "boolean",
256
268
  "default": false,
257
269
  "description": "Whether this state prevents all user interaction. See spec/accessibility.md."
270
+ },
271
+ "lifecycle": {
272
+ "$ref": "#/$defs/lifecycle",
273
+ "description": "Version lifecycle metadata for this state. Set lifecycle.deprecated to signal that tokens referencing this state should migrate."
258
274
  }
259
275
  },
260
276
  "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
@@ -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,13 +88,14 @@ 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
+ | `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`). |
98
+ | `deprecatedEnumValues` | object | OPTIONAL | Per-enum-value lifecycle metadata. Keys are enum value strings; values are lifecycle objects. Values absent from this map are not deprecated. |
98
99
 
99
100
  **NORMATIVE:** Each key in `options` **MUST** be camelCase.
100
101
 
@@ -102,6 +103,23 @@ An option descriptor is a JSON object with the following fields:
102
103
 
103
104
  **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.
104
105
 
106
+ **ADVISORY:** When a `deprecatedEnumValues` entry exists for a value that a non-deprecated token references via its `name` object field for that option, SPEC-037 fires an advisory warning. Keys in `deprecatedEnumValues` are not independently validated against `enum` — entries for values absent from `enum` are silently ignored by validators (a future rule may close this gap).
107
+
108
+ Example with a deprecated enum value:
109
+
110
+ ```json
111
+ "variant": {
112
+ "type": "string",
113
+ "enum": ["primary", "secondary", "cta"],
114
+ "deprecatedEnumValues": {
115
+ "cta": {
116
+ "deprecated": "1.0.0-draft",
117
+ "deprecatedComment": "Use primary instead."
118
+ }
119
+ }
120
+ }
121
+ ```
122
+
105
123
  ```json
106
124
  "options": {
107
125
  "variant": {
@@ -186,12 +204,13 @@ The `anatomy` block declares the component's named **visual parts** — the anat
186
204
 
187
205
  Each anatomy part carries at minimum:
188
206
 
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. |
207
+ | Field | Type | Required | Description |
208
+ | ------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
209
+ | `name` | string | REQUIRED | Anatomy part identifier (e.g. `icon`, `label`, `handle`). |
210
+ | `description` | string | OPTIONAL | Plain-text description of the part. |
211
+ | `required` | boolean | OPTIONAL | Whether this part is always present. Default: `false`. |
212
+ | `contains` | array of strings | OPTIONAL | Informative: other anatomy part names nested within this part. |
213
+ | `lifecycle` | object | OPTIONAL | Version lifecycle metadata for this part. When `lifecycle.deprecated` is set, SPEC-037 fires on referencing tokens. |
195
214
 
196
215
  See [`spec/anatomy-format.md`](anatomy-format.md) for constraints, cross-field validation, and the full anatomy part schema.
197
216
 
@@ -216,6 +235,7 @@ Each state carries at minimum:
216
235
  | `trigger` | string | OPTIONAL | `"prop"` for persistent prop-driven states (e.g. `isDisabled`) or `"interaction"` for runtime interaction states (hover, focus, pressed). |
217
236
  | `precedence` | integer | OPTIONAL | Resolution precedence; higher integer wins when multiple states are active. |
218
237
  | `layered` | boolean | OPTIONAL | `true` for states that compose with others (e.g. focus ring over hover). Default: `false`. |
238
+ | `lifecycle` | object | OPTIONAL | Version lifecycle metadata for this state. When `lifecycle.deprecated` is set, SPEC-037 fires on referencing tokens. |
219
239
 
220
240
  See [`spec/state-model.md`](state-model.md) for the full state resolution algorithm, trigger semantics, and precedence rules.
221
241
 
@@ -271,14 +291,16 @@ The `context` field is informative. It is used by `describe_component` (Phase 8
271
291
 
272
292
  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
293
 
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). |
294
+ | Rule ID | Name | Severity | Assert |
295
+ | -------- | -------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
296
+ | SPEC-018 | `component-name-exists` | error | Token `component` field value **MUST** match the `name` of a declared component in the dataset. |
297
+ | 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). |
298
+ | SPEC-020 | `component-anatomy-valid` | error | Token `anatomy` field value **MUST** match the `name` of a declared anatomy part on the referenced component. |
299
+ | 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. |
300
+ | 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). |
301
+ | 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). |
302
+ | 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. |
303
+ | SPEC-037 | `sub-entity-deprecation-cascade` | warning | A non-deprecated token **SHOULD NOT** reference a deprecated anatomy part, state, or option-enum value via `name.*`. Advisory warning prompts migration. Requires `lifecycle` on anatomy/state or `deprecatedEnumValues` on option descriptor. |
282
304
 
283
305
  ## Full example
284
306
 
@@ -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