@adobe/design-data-spec 0.1.1 → 0.3.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.
- package/README.md +3 -3
- package/components/button.json +70 -0
- package/conformance/README.md +0 -1
- package/conformance/invalid/SPEC-016/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-016/tokens.tokens.json +8 -0
- package/conformance/invalid/SPEC-017/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-017/tokens.tokens.json +7 -0
- package/conformance/invalid/SPEC-018/dataset.json +9 -0
- package/conformance/invalid/SPEC-018/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-019/dataset.json +29 -0
- package/conformance/invalid/SPEC-019/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-020/dataset.json +27 -0
- package/conformance/invalid/SPEC-020/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-021/dataset.json +18 -0
- package/conformance/invalid/SPEC-021/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-022/dataset.json +33 -0
- package/conformance/invalid/SPEC-022/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-023/dataset.json +18 -0
- package/conformance/invalid/SPEC-023/expected-errors.json +10 -0
- package/conformance/invalid/SPEC-024/dataset.json +18 -0
- package/conformance/invalid/SPEC-024/expected-errors.json +10 -0
- package/conformance/valid/component-refs/dataset.json +63 -0
- package/conformance/valid/composite-drop-shadow.json +14 -0
- package/conformance/valid/composite-typography-scale.json +6 -0
- package/conformance/valid/composite-typography.json +12 -0
- package/conformance/valid/string-name-escape-hatch.json +7 -0
- package/fields/scaleIndex.json +15 -0
- package/package.json +18 -6
- package/rules/rules.yaml +113 -5
- package/schemas/anatomy-part.schema.json +35 -0
- package/schemas/component.schema.json +267 -0
- package/schemas/field.schema.json +2 -2
- package/schemas/state-declaration.schema.json +36 -0
- package/schemas/token.schema.json +26 -10
- package/schemas/value-types/drop-shadow.schema.json +20 -0
- package/schemas/value-types/typography-scale.schema.json +13 -0
- package/schemas/value-types/typography.schema.json +16 -0
- package/spec/agent-surface.md +116 -0
- package/spec/anatomy-format.md +167 -0
- package/spec/component-format.md +326 -0
- package/spec/evolution.md +0 -1
- package/spec/index.md +27 -21
- package/spec/state-model.md +245 -0
- package/spec/token-format.md +80 -15
- package/src/canonical.js +61 -0
- package/src/validate.js +166 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Agent-readable surface
|
|
2
|
+
|
|
3
|
+
**Spec version:** `1.0.0-draft` (see [Overview](index.md))\
|
|
4
|
+
**Status:** Draft — RFC-C / Phase 8. Surface and tool list are subject to change. No reference implementation exists yet.
|
|
5
|
+
|
|
6
|
+
This document defines the **agent-readable surface**: the contract an external AI agent uses to consume design data conformant with this specification. It standardizes a small set of operations exposed through three transports — a CLI, a Model Context Protocol (MCP) server, and a Claude Code Agent Skill — all backed by the same resolver, validator, and query implementations that power the rest of the SDK.
|
|
7
|
+
|
|
8
|
+
## Goals
|
|
9
|
+
|
|
10
|
+
The surface targets three consumer shapes:
|
|
11
|
+
|
|
12
|
+
1. **Authoring an external system.** A non-Spectrum design system being constructed inside an AI tool (e.g. a finance-dashboard prototype tool) wants to produce spec-conformant tokens, dimensions, and components without re-deriving the format from prose.
|
|
13
|
+
2. **Extending Spectrum.** A product or platform team adds tokens, components, or overrides on top of the published Spectrum foundation and needs to validate that the additions cascade and resolve correctly.
|
|
14
|
+
3. **Adhering to Spectrum.** A prototyping tool generates UI that should match Spectrum even where no bound component exists (e.g. CSS for a custom card). The agent needs to look up tokens by intent, validate proposed property values against the foundation, and report drift.
|
|
15
|
+
|
|
16
|
+
**NORMATIVE:** A conforming agent surface implementation MUST support all three consumer shapes, parameterized by the manifest provided at session start (see [Session primer](#session-primer)).
|
|
17
|
+
|
|
18
|
+
## Non-goals
|
|
19
|
+
|
|
20
|
+
* Generating finished platform code (Swift, CSS, etc.). Output formatting is the consumer's responsibility; the surface returns structured tokens.
|
|
21
|
+
* Hosting design data. The surface operates on local datasets reachable from the consumer's filesystem, plus optionally a remote published manifest.
|
|
22
|
+
* Replacing the existing Spectrum-bound tools (`@adobe/spectrum-design-data-mcp`, `@adobe/s2-docs-mcp`). Those expose Spectrum-specific shapes; this surface is generic to any spec-conformant dataset.
|
|
23
|
+
|
|
24
|
+
## Tool catalog
|
|
25
|
+
|
|
26
|
+
**NORMATIVE:** A conforming implementation MUST expose the following operations. Transport-specific naming (CLI subcommand vs MCP tool name vs skill action) MAY differ; the semantics MUST NOT.
|
|
27
|
+
|
|
28
|
+
| Operation | Reads | Returns | Backed by |
|
|
29
|
+
| -------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | ---------------------- |
|
|
30
|
+
| `resolve_token` | property + dimension context | winning token (literal or resolved alias) with file/UUID/specificity | `cascade::resolve` |
|
|
31
|
+
| `query_tokens` | filter expression (see [Query](query.md)) | matching token list | `query::filter` |
|
|
32
|
+
| `validate_usage` | candidate token document or fragment | `ValidationReport` (Layer 1 + Layer 2 diagnostics) | `validate::validate_*` |
|
|
33
|
+
| `describe_component` | component identifier | component contract (anatomy, options, states); see [#832](https://github.com/adobe/spectrum-design-data/discussions/832) | (Phase 6 contract) |
|
|
34
|
+
| `suggest_token` | natural-language intent + optional property hint | ranked candidate tokens with rationale (RECOMMENDED, not NORMATIVE in v1) | registry + query |
|
|
35
|
+
| `get_guidance` | token UUID, component identifier, or anatomy reference | attached document blocks (Phase 9 / RFC-D); falls back to empty list pre-RFC-D | document blocks |
|
|
36
|
+
| `diff_datasets` | two dataset roots | `DiffReport` per [Diff](diff.md) | `diff::semantic_diff` |
|
|
37
|
+
|
|
38
|
+
**NORMATIVE:** `validate_usage`, `resolve_token`, `query_tokens`, and `diff_datasets` MUST be implemented in a conforming agent surface. `suggest_token` and `get_guidance` are RECOMMENDED. `describe_component` becomes NORMATIVE once [#832](https://github.com/adobe/spectrum-design-data/discussions/832) reaches implemented status.
|
|
39
|
+
|
|
40
|
+
## Session primer
|
|
41
|
+
|
|
42
|
+
An agent loop benefits from a small, structural overview at session start so that subsequent calls are well-scoped. The **session primer** is a single operation that returns a serialized summary of the active dataset.
|
|
43
|
+
|
|
44
|
+
**NORMATIVE:** A conforming implementation MUST expose a `primer` operation returning a JSON object with at least:
|
|
45
|
+
|
|
46
|
+
| Field | Type | Meaning |
|
|
47
|
+
| ---------------- | -------------- | ----------------------------------------------------------------- |
|
|
48
|
+
| `specVersion` | string | The spec version the dataset declares (see [Overview](index.md)). |
|
|
49
|
+
| `manifest` | object \| null | Resolved platform manifest, if any (see [Manifest](manifest.md)). |
|
|
50
|
+
| `dimensions` | array | Declared dimensions with modes and defaults. |
|
|
51
|
+
| `components` | array | Component identifiers exposed by the dataset (post Phase 6). |
|
|
52
|
+
| `taxonomyFields` | array | Active name-object fields and their declared vocabulary. |
|
|
53
|
+
| `tokenCount` | integer | Total tokens in the merged cascade. |
|
|
54
|
+
|
|
55
|
+
**RECOMMENDED:** The primer payload SHOULD fit within 2,000 tokens of LLM context for typical Spectrum-scale datasets. Implementations that produce larger payloads SHOULD provide a `--summary` mode.
|
|
56
|
+
|
|
57
|
+
**RATIONALE:** The primer replaces the "DESIGN.md emit" approach explored in earlier RFC-C drafts. A live, queryable primer keeps the agent's session context current without requiring the agent to maintain a frozen prose summary that can drift from the dataset.
|
|
58
|
+
|
|
59
|
+
## Transports
|
|
60
|
+
|
|
61
|
+
**NORMATIVE:** A conforming implementation MUST expose the [Tool catalog](#tool-catalog) through at least one of the following transports.
|
|
62
|
+
|
|
63
|
+
### CLI
|
|
64
|
+
|
|
65
|
+
The reference CLI is `design-data` (see [`sdk/cli/`](../../../sdk/cli/)). RFC-C extends the existing subcommands (`validate`, `resolve`, `diff`, `query`) with:
|
|
66
|
+
|
|
67
|
+
* `design-data primer [PATH]` — emit the [Session primer](#session-primer) payload.
|
|
68
|
+
* `design-data suggest "<intent>" [--property <hint>]` — invoke `suggest_token`.
|
|
69
|
+
* `design-data explain <token-uuid|component-id>` — invoke `get_guidance`.
|
|
70
|
+
|
|
71
|
+
**NORMATIVE:** All RFC-C CLI output MUST default to JSON when stdout is not a TTY, and MUST emit human-friendly output when stdout is a TTY. This makes the CLI directly composable from agent shells without per-call format flags.
|
|
72
|
+
|
|
73
|
+
### MCP server
|
|
74
|
+
|
|
75
|
+
A reference MCP server is RECOMMENDED to ship as `@adobe/design-data-agent-mcp` (separate from the existing Spectrum-bound `@adobe/spectrum-design-data-mcp` to avoid coupling to a specific dataset).
|
|
76
|
+
|
|
77
|
+
**NORMATIVE:** The MCP server MUST register one tool per [Tool catalog](#tool-catalog) operation. Tool names MUST match the operation name in the table verbatim. Each tool's input schema MUST mirror the CLI flag set; agents that learn one transport SHOULD work with the other.
|
|
78
|
+
|
|
79
|
+
### Agent skill
|
|
80
|
+
|
|
81
|
+
A reference Claude Code Agent Skill is RECOMMENDED at `tools/design-data-agent-mcp/skills/design-data/SKILL.md`. The skill SHOULD trigger on intent words covering all three [Goals](#goals) — for example "design system", "design tokens", "drift", "spec-conformant", and explicit Spectrum mentions when the active manifest binds Spectrum.
|
|
82
|
+
|
|
83
|
+
**RECOMMENDED:** The skill SHOULD shell out to the CLI rather than embedding tool calls, so its description (the only persistent context cost) stays small and the heavy lifting happens out-of-band.
|
|
84
|
+
|
|
85
|
+
## Conformance
|
|
86
|
+
|
|
87
|
+
**NORMATIVE:** An agent surface implementation that claims conformance MUST:
|
|
88
|
+
|
|
89
|
+
1. Implement all NORMATIVE operations in the [Tool catalog](#tool-catalog).
|
|
90
|
+
2. Implement the [Session primer](#session-primer) operation.
|
|
91
|
+
3. Expose the catalog through at least one transport in [Transports](#transports).
|
|
92
|
+
4. Emit `validate_usage` diagnostics that match the structure produced by `validate::validate_all_with_options` (see `sdk/core/src/report.rs`): each diagnostic MUST include `rule_id`, `severity`, `message`, `instance_path`, `schema_path`, `file`, and `token` where applicable.
|
|
93
|
+
5. Honor manifest-based filtering: when a manifest is present in the session, `query_tokens` and `resolve_token` MUST respect `include`/`exclude` and MUST apply manifest overrides before returning results.
|
|
94
|
+
|
|
95
|
+
## Worked example
|
|
96
|
+
|
|
97
|
+
The following sketch shows an agent loop that adheres-to-Spectrum while authoring a non-bound component.
|
|
98
|
+
|
|
99
|
+
1. Agent calls `primer ./spectrum-data` and learns that `colorScheme` and `scale` are the active dimensions and that `button`, `picker`, and `card` are exposed components.
|
|
100
|
+
2. User asks for a "subtle hover background for a card on dark mode".
|
|
101
|
+
3. Agent calls `suggest_token "subtle hover background for card" --property background-color`.
|
|
102
|
+
4. Surface returns a ranked list including `background-color-hover` from Spectrum foundation and `background-color-card-hover` from a card component group.
|
|
103
|
+
5. Agent calls `resolve_token background-color-card-hover --color-scheme dark`.
|
|
104
|
+
6. Surface returns the resolved literal value for the dark-mode card hover background.
|
|
105
|
+
7. Agent emits CSS using that value and calls `validate_usage` against the proposed token document fragment to confirm no SPEC-NNN diagnostics fire.
|
|
106
|
+
|
|
107
|
+
The same loop, with a different manifest passed at primer time, authors a non-Spectrum system: the surface returns whatever tokens that dataset declares, and `validate_usage` enforces the same spec rules independent of which design system is in play.
|
|
108
|
+
|
|
109
|
+
## References
|
|
110
|
+
|
|
111
|
+
* [#714 — Design Data Specification (umbrella)](https://github.com/adobe/spectrum-design-data/discussions/714)
|
|
112
|
+
* [#625 — Token Authoring Workflow](https://github.com/adobe/spectrum-design-data/discussions/625)
|
|
113
|
+
* [#832 — Component Contract in Design Data Spec](https://github.com/adobe/spectrum-design-data/discussions/832) — supplies `describe_component` once implemented
|
|
114
|
+
* [Document blocks](document-blocks.md) (forthcoming, RFC-D / Phase 9) — supplies `get_guidance` payloads
|
|
115
|
+
* [`sdk/cli/src/main.rs`](../../../sdk/cli/src/main.rs) — CLI surface to extend
|
|
116
|
+
* [`sdk/core/src/report.rs`](../../../sdk/core/src/report.rs) — diagnostic shape
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Anatomy format
|
|
2
|
+
|
|
3
|
+
**Spec version:** `1.0.0-draft` (see [Overview](index.md))
|
|
4
|
+
|
|
5
|
+
This document defines the normative **anatomy part declaration** object: the named visual sub-parts of a component that appear as values of the `anatomy` field in token name objects. Anatomy declarations complete the machine-readable contract introduced by the component declaration (see [Component format — Anatomy stub](component-format.md#anatomy-stub)) and enable cross-reference validation between tokens and component surfaces.
|
|
6
|
+
|
|
7
|
+
Scoped under [RFC-A — Component Contract in Design Data Spec](https://github.com/adobe/spectrum-design-data/discussions/832). See also [Component format](component-format.md).
|
|
8
|
+
|
|
9
|
+
## Anatomy part object
|
|
10
|
+
|
|
11
|
+
An anatomy part is a **JSON object** that appears as an element of a component declaration's `anatomy` array. Each anatomy part object **MUST** validate against the standalone schema [`anatomy-part.schema.json`](../schemas/anatomy-part.schema.json) (canonical `$id`: `https://opensource.adobe.com/spectrum-design-data/schemas/v0/anatomy-part.schema.json`).
|
|
12
|
+
|
|
13
|
+
**NORMATIVE:** `anatomy` **MUST** be a JSON array within the component declaration. Each element **MUST** be an anatomy part object.
|
|
14
|
+
|
|
15
|
+
### Fields
|
|
16
|
+
|
|
17
|
+
| Field | Type | Required | Description |
|
|
18
|
+
| ------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
19
|
+
| `name` | string | REQUIRED | Kebab-case identifier. **MUST** match the pattern `^[a-z][a-z0-9-]*$`. |
|
|
20
|
+
| `description` | string | OPTIONAL | Plain-text description of the part's visual role and boundaries. |
|
|
21
|
+
| `required` | boolean | OPTIONAL | Whether this part is always rendered regardless of configuration. Default: `false`. |
|
|
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
|
+
|
|
24
|
+
**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
|
+
### `name`
|
|
27
|
+
|
|
28
|
+
**NORMATIVE:** `name` **MUST** match the pattern `^[a-z][a-z0-9-]*$` — lower-case kebab-case, non-empty.
|
|
29
|
+
|
|
30
|
+
**NORMATIVE:** `name` **MUST** be unique within the `anatomy` array of a single component declaration (rule SPEC-024). Duplicate `name` values on the same component are a validation error.
|
|
31
|
+
|
|
32
|
+
**NORMATIVE:** Token name-object `anatomy` field values referencing a component **MUST** match the `name` of a declared anatomy part on that component (rule SPEC-020). An undeclared `anatomy` value is a validation error.
|
|
33
|
+
|
|
34
|
+
### `description`
|
|
35
|
+
|
|
36
|
+
**OPTIONAL.** A plain-text description of the anatomy part's visual role (e.g. `"Background fill track behind the progress indicator."`).
|
|
37
|
+
|
|
38
|
+
**RECOMMENDED:** Custom anatomy part names (those outside the [canonical anatomy vocabulary](#canonical-anatomy-vocabulary)) **SHOULD** include a `description` to document intent (rule SPEC-023 fires a warning for undocumented custom names).
|
|
39
|
+
|
|
40
|
+
### `required`
|
|
41
|
+
|
|
42
|
+
**OPTIONAL.** A boolean indicating whether the anatomy part is always present in the component's rendered output, regardless of its configuration options. Defaults to `false`.
|
|
43
|
+
|
|
44
|
+
When `required` is `true`, the anatomy part is unconditionally rendered (e.g. a `label` that cannot be hidden). When `false` or omitted, the part may or may not appear depending on component props.
|
|
45
|
+
|
|
46
|
+
### `contains`
|
|
47
|
+
|
|
48
|
+
**OPTIONAL.** An informative list of child anatomy part `name` values that are visually or structurally nested within this part. This field is for documentation and tooling assistance; it does not carry enforcement semantics.
|
|
49
|
+
|
|
50
|
+
**RECOMMENDED:** When a part logically encloses other declared anatomy parts, authors **SHOULD** use `contains` to make the nesting explicit.
|
|
51
|
+
|
|
52
|
+
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
|
+
```json
|
|
55
|
+
"anatomy": [
|
|
56
|
+
{
|
|
57
|
+
"name": "field",
|
|
58
|
+
"description": "Input field wrapper enclosing the label and help text.",
|
|
59
|
+
"contains": ["label", "help-text"]
|
|
60
|
+
},
|
|
61
|
+
{ "name": "label", "description": "Primary text label.", "required": true },
|
|
62
|
+
{ "name": "help-text", "description": "Guidance text below the field." }
|
|
63
|
+
]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Canonical anatomy vocabulary
|
|
67
|
+
|
|
68
|
+
The following anatomy part names are defined by the cross-platform design audit and **SHOULD** be used in preference to custom names when their semantics match. Using canonical names enables cross-component tooling, documentation generation, and token audits.
|
|
69
|
+
|
|
70
|
+
| Name | Semantics |
|
|
71
|
+
| --------------------- | ------------------------------------------------------------------------ |
|
|
72
|
+
| `body` | Primary content area of a component (e.g. card body, dialog body). |
|
|
73
|
+
| `checkmark` | Selection indicator used in checkbox or radio components. |
|
|
74
|
+
| `disclosure-triangle` | Expand/collapse indicator for accordion, tree, or disclosure components. |
|
|
75
|
+
| `field` | Input field wrapper (encloses label, input surface, and help text). |
|
|
76
|
+
| `handle` | Drag handle for resizable or sortable components. |
|
|
77
|
+
| `header` | Top section of a panel, card, or dialog. |
|
|
78
|
+
| `icon` | Decorative or semantic icon placed within a component. |
|
|
79
|
+
| `label` | Primary text label identifying the component or its value. |
|
|
80
|
+
| `picker` | Dropdown trigger area (the visible affordance, not the overlay). |
|
|
81
|
+
| `progress-bar` | Visual progress fill track indicating completion level. |
|
|
82
|
+
| `swatch` | Color or pattern preview area. |
|
|
83
|
+
| `thumbnail` | Image preview area within a component. |
|
|
84
|
+
| `track` | Background rail for slider or progress bar components. |
|
|
85
|
+
| `value` | Numeric or text display value shown within a component. |
|
|
86
|
+
|
|
87
|
+
Custom part names are permitted. When a custom name is used, the anatomy part object **SHOULD** include a `description` field explaining its visual role (rule SPEC-023).
|
|
88
|
+
|
|
89
|
+
## Cross-reference with token name objects
|
|
90
|
+
|
|
91
|
+
Token name objects use an `anatomy` field to scope a token to a specific visible part of a component. The `anatomy` field value must correspond to a part declared in the component's `anatomy` array.
|
|
92
|
+
|
|
93
|
+
**NORMATIVE:** A token name-object `anatomy` field value **MUST** match the `name` of a declared anatomy part on the component identified by the token's `component` field (rule SPEC-020). An `anatomy` value that does not match any declared part name is a validation error.
|
|
94
|
+
|
|
95
|
+
See [Token format — Name object](token-format.md#name-object) for the full name object field catalog.
|
|
96
|
+
|
|
97
|
+
**NORMATIVE:** The `anatomy` field in a token name object **MUST NOT** be present unless the token's `component` field is also present (rule SPEC-025). Anatomy is always scoped to a component.
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"name": {
|
|
102
|
+
"component": "slider",
|
|
103
|
+
"anatomy": "handle",
|
|
104
|
+
"property": "background-color",
|
|
105
|
+
"state": "hover"
|
|
106
|
+
},
|
|
107
|
+
"value": "#0265dc"
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## SPEC rules
|
|
112
|
+
|
|
113
|
+
The following rules in the Layer 2 rule catalog (`rules/rules.yaml`) apply to anatomy part declarations. SPEC-020 was introduced in Phase 6.1 (component-format); SPEC-023, SPEC-024, and SPEC-025 are introduced by this chapter.
|
|
114
|
+
|
|
115
|
+
| Rule ID | Name | Severity | Assert |
|
|
116
|
+
| -------- | -------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
117
|
+
| SPEC-020 | `component-anatomy-valid` | error | Token `anatomy` field value **MUST** match the `name` of a declared anatomy part on the referenced component. |
|
|
118
|
+
| SPEC-023 | `anatomy-custom-part-documented` | warning | Anatomy part declarations with a `name` outside the canonical anatomy vocabulary **SHOULD** include a `description` field documenting the part's purpose. |
|
|
119
|
+
| SPEC-024 | `anatomy-part-name-unique` | error | Anatomy part `name` values **MUST** be unique within a single component's `anatomy` array. |
|
|
120
|
+
| SPEC-025 | `anatomy-requires-component` | error | A token name object **MUST NOT** include an `anatomy` field unless a `component` field is also present. |
|
|
121
|
+
|
|
122
|
+
## Full example
|
|
123
|
+
|
|
124
|
+
A complete `anatomy` array for a slider component, demonstrating canonical names, a custom name, and `contains` usage:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
"anatomy": [
|
|
128
|
+
{
|
|
129
|
+
"name": "track",
|
|
130
|
+
"description": "Background rail spanning the slider's full range.",
|
|
131
|
+
"required": true
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"name": "progress-bar",
|
|
135
|
+
"description": "Filled portion of the track indicating the current value.",
|
|
136
|
+
"required": true
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"name": "handle",
|
|
140
|
+
"description": "Draggable thumb positioned at the current value.",
|
|
141
|
+
"required": true
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"name": "label",
|
|
145
|
+
"description": "Text label identifying the slider.",
|
|
146
|
+
"required": false
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "value",
|
|
150
|
+
"description": "Numeric display of the current slider value.",
|
|
151
|
+
"required": false
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"name": "tick-marks",
|
|
155
|
+
"description": "Discrete step indicators along the track. Present only when step markers are enabled.",
|
|
156
|
+
"required": false
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "range-group",
|
|
160
|
+
"description": "Wrapper grouping the track and both handles for a range slider variant.",
|
|
161
|
+
"required": false,
|
|
162
|
+
"contains": ["track", "progress-bar", "handle"]
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
In this example, `tick-marks` and `range-group` are custom names outside the canonical vocabulary. Both include a `description` to satisfy rule SPEC-023. The `range-group` part uses `contains` to declare that it encloses `track`, `progress-bar`, and `handle`.
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# Component format
|
|
2
|
+
|
|
3
|
+
**Spec version:** `1.0.0-draft` (see [Overview](index.md))
|
|
4
|
+
|
|
5
|
+
This document defines the normative **component declaration** object: identity (`$id`, `name`, `displayName`), component metadata (`meta`), API options (`options`), named content slots (`slots`), anatomy parts (`anatomy`), state model (`states`), and lifecycle metadata.
|
|
6
|
+
|
|
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
|
+
|
|
9
|
+
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
|
+
|
|
11
|
+
## Document shape
|
|
12
|
+
|
|
13
|
+
A component declaration is a **single JSON object** in a `.json` file. One file per component. Files live under a `components/` directory within a design-data package.
|
|
14
|
+
|
|
15
|
+
**NORMATIVE:** Each component declaration file **MUST** validate against the Layer 1 schema [`component.schema.json`](../schemas/component.schema.json) (canonical `$id`: `https://opensource.adobe.com/spectrum-design-data/schemas/v0/component.schema.json`). Layer 1 and Layer 2 validation are defined in the [validation layers](index.md#validation-layers) section of the overview.
|
|
16
|
+
|
|
17
|
+
## Component object
|
|
18
|
+
|
|
19
|
+
### Required fields
|
|
20
|
+
|
|
21
|
+
A component declaration **MUST** contain:
|
|
22
|
+
|
|
23
|
+
| Field | Type | Description |
|
|
24
|
+
| ------------- | ----------------- | ------------------------------------------------------------------------------------- |
|
|
25
|
+
| `$id` | URI string | Canonical identifier for this component declaration. |
|
|
26
|
+
| `name` | kebab-case string | Machine identifier; used as the value of the `component` field in token name objects. |
|
|
27
|
+
| `displayName` | string | Human-readable component name (e.g. `"Button"`). |
|
|
28
|
+
| `meta` | object | Category and documentation link — see [Meta](#meta). |
|
|
29
|
+
|
|
30
|
+
### Optional fields
|
|
31
|
+
|
|
32
|
+
| Field | Type | Description |
|
|
33
|
+
| ------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
34
|
+
| `specVersion` | string | Declares which spec version this document targets. Currently `"1.0.0-draft"`; future stable releases will accept their own version string. |
|
|
35
|
+
| `description` | string | Plain-text description of the component's purpose. |
|
|
36
|
+
| `options` | object | Component API options — see [Options](#options). |
|
|
37
|
+
| `slots` | array | Named content injection points — see [Slots](#slots). |
|
|
38
|
+
| `anatomy` | array | Named anatomy parts — see [Anatomy (stub)](#anatomy-stub). |
|
|
39
|
+
| `states` | array | Per-component state declarations — see [States (stub)](#states-stub). |
|
|
40
|
+
| `lifecycle` | object | Version lifecycle metadata — see [Lifecycle](#lifecycle). |
|
|
41
|
+
|
|
42
|
+
**NORMATIVE:** No properties beyond those listed above are permitted at the top level of a component declaration. Additional fields **MUST** cause a Layer 1 schema error.
|
|
43
|
+
|
|
44
|
+
### `$id`
|
|
45
|
+
|
|
46
|
+
**NORMATIVE:** The `$id` **MUST** be a valid URI identifying this component declaration document. The recommended pattern is:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/{name}.json
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
where `{name}` matches the component's `name` field.
|
|
53
|
+
|
|
54
|
+
### `name`
|
|
55
|
+
|
|
56
|
+
**NORMATIVE:** `name` **MUST** match the pattern `^[a-z][a-z0-9-]*$` — lower-case kebab-case, non-empty.
|
|
57
|
+
|
|
58
|
+
**NORMATIVE:** `name` **MUST** be unique within a dataset. No two component declarations in the same design-data package may share a `name` value.
|
|
59
|
+
|
|
60
|
+
**NORMATIVE:** Token name-object `component` field values **MUST** match the `name` of a declared component in the dataset (rule SPEC-018). An undeclared `component` value is a validation error.
|
|
61
|
+
|
|
62
|
+
### Meta
|
|
63
|
+
|
|
64
|
+
**NORMATIVE:** `meta` **MUST** contain:
|
|
65
|
+
|
|
66
|
+
| Field | Type | Values |
|
|
67
|
+
| ------------------ | ------------- | --------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `category` | string (enum) | `actions`, `containers`, `data visualization`, `feedback`, `inputs`, `navigation`, `status`, `typography` |
|
|
69
|
+
| `documentationUrl` | URI string | Link to the component's documentation page. |
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
"meta": {
|
|
73
|
+
"category": "actions",
|
|
74
|
+
"documentationUrl": "https://spectrum.adobe.com/page/button/"
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Options
|
|
79
|
+
|
|
80
|
+
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.
|
|
81
|
+
|
|
82
|
+
**NORMATIVE:** `options` **MUST** be a JSON object. Each key is an option name; each value is an **option descriptor**.
|
|
83
|
+
|
|
84
|
+
### Option descriptor
|
|
85
|
+
|
|
86
|
+
An option descriptor is a JSON object with the following fields:
|
|
87
|
+
|
|
88
|
+
| Field | Type | Required | Description |
|
|
89
|
+
| ------------- | --------------- | -------- | -------------------------------------------------------------------------------- |
|
|
90
|
+
| `type` | string or array | OPTIONAL | JSON Schema primitive type(s): `"string"`, `"boolean"`, `"number"`, `"integer"`. |
|
|
91
|
+
| `enum` | array | OPTIONAL | Exhaustive list of permitted values. |
|
|
92
|
+
| `default` | any | OPTIONAL | Default value when the option is not specified. |
|
|
93
|
+
| `description` | string | OPTIONAL | Plain-text description of what the option controls. |
|
|
94
|
+
| `$ref` | URI string | OPTIONAL | Reference to a shared type schema (e.g. `workflow-icon.json`). |
|
|
95
|
+
|
|
96
|
+
**NORMATIVE:** Each key in `options` **MUST** be camelCase.
|
|
97
|
+
|
|
98
|
+
**NORMATIVE:** Boolean option names **MUST** begin with `is` or `has` (e.g. `isDisabled`, `hasIcon`).
|
|
99
|
+
|
|
100
|
+
**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.
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
"options": {
|
|
104
|
+
"variant": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"enum": ["accent", "negative", "primary", "secondary"],
|
|
107
|
+
"default": "accent",
|
|
108
|
+
"description": "Visual emphasis level."
|
|
109
|
+
},
|
|
110
|
+
"size": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"enum": ["s", "m", "l", "xl"],
|
|
113
|
+
"default": "m"
|
|
114
|
+
},
|
|
115
|
+
"isDisabled": {
|
|
116
|
+
"type": "boolean",
|
|
117
|
+
"default": false
|
|
118
|
+
},
|
|
119
|
+
"icon": {
|
|
120
|
+
"$ref": "https://opensource.adobe.com/spectrum-design-data/schemas/types/workflow-icon.json",
|
|
121
|
+
"description": "Icon placed at the start of the button. Required when hideLabel is true."
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Slots
|
|
127
|
+
|
|
128
|
+
The `slots` block declares the component's named **content injection points** — the positions where consumers place child content. Slot declarations are derived from the cross-platform audit in [`audits/slots.audit.md`](../audits/slots.audit.md).
|
|
129
|
+
|
|
130
|
+
**NORMATIVE:** `slots` **MUST** be a JSON array. Each element is a **slot declaration**.
|
|
131
|
+
|
|
132
|
+
### Slot declaration
|
|
133
|
+
|
|
134
|
+
| Field | Type | Required | Description |
|
|
135
|
+
| ------------- | ------- | -------- | --------------------------------------------------------------------------- |
|
|
136
|
+
| `name` | string | REQUIRED | Slot identifier. **SHOULD** come from the canonical vocabulary (see below). |
|
|
137
|
+
| `description` | string | OPTIONAL | Plain-text description of what content goes in this slot. |
|
|
138
|
+
| `required` | boolean | OPTIONAL | Whether consumers **MUST** populate this slot. Default: `false`. |
|
|
139
|
+
|
|
140
|
+
### Canonical slot vocabulary
|
|
141
|
+
|
|
142
|
+
The following slot names are defined by the cross-platform audit and **SHOULD** be used in preference to custom names:
|
|
143
|
+
|
|
144
|
+
| Name | Semantics |
|
|
145
|
+
| -------------------- | ----------------------------------------------------------------------- |
|
|
146
|
+
| `default` | Primary content (text, children). The main content slot. |
|
|
147
|
+
| `icon` | Decorative icon at the leading edge of the component. |
|
|
148
|
+
| `label` | Human-readable identifier / placeholder text (distinct from `default`). |
|
|
149
|
+
| `help-text` | Non-error guidance text below a form field. |
|
|
150
|
+
| `negative-help-text` | Validation error message for a form field. |
|
|
151
|
+
| `action` | Secondary interactive button or call-to-action. |
|
|
152
|
+
| `heading` | Section or dialog heading. |
|
|
153
|
+
| `description` | Section or dialog body text (distinct from `help-text`). |
|
|
154
|
+
| `hero` | Large header media (e.g. dialog hero image). |
|
|
155
|
+
| `footer` | Below-content supplemental area (e.g. dialog footer). |
|
|
156
|
+
| `tooltip` | Floating annotation attached to the component. |
|
|
157
|
+
|
|
158
|
+
**NORMATIVE:** Custom slot names are permitted but **SHOULD** be documented in the slot's `description` field (rule SPEC-021 fires a warning for undocumented custom names).
|
|
159
|
+
|
|
160
|
+
**RECOMMENDED:** Components **SHOULD** declare a `default` slot when they accept primary child content.
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
"slots": [
|
|
164
|
+
{
|
|
165
|
+
"name": "default",
|
|
166
|
+
"description": "Text label of the button.",
|
|
167
|
+
"required": false
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"name": "icon",
|
|
171
|
+
"description": "Icon placed at the start of the button. Required when isLabelHidden is true."
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Anatomy (stub)
|
|
177
|
+
|
|
178
|
+
The `anatomy` block declares the component's named **visual parts** — the anatomy terms used in token name-object `anatomy` fields. Full normative definition is in [`spec/anatomy-format.md`](anatomy-format.md) (Phase 6.2).
|
|
179
|
+
|
|
180
|
+
**NORMATIVE:** `anatomy` **MUST** be a JSON array. Each element is an anatomy part object.
|
|
181
|
+
|
|
182
|
+
**NORMATIVE:** Token name-object `anatomy` field values referencing this component **MUST** match the `name` of a declared anatomy part (rule SPEC-020).
|
|
183
|
+
|
|
184
|
+
Each anatomy part carries at minimum:
|
|
185
|
+
|
|
186
|
+
| Field | Type | Required | Description |
|
|
187
|
+
| ------------- | ---------------- | -------- | -------------------------------------------------------------- |
|
|
188
|
+
| `name` | string | REQUIRED | Anatomy part identifier (e.g. `icon`, `label`, `handle`). |
|
|
189
|
+
| `description` | string | OPTIONAL | Plain-text description of the part. |
|
|
190
|
+
| `required` | boolean | OPTIONAL | Whether this part is always present. Default: `false`. |
|
|
191
|
+
| `contains` | array of strings | OPTIONAL | Informative: other anatomy part names nested within this part. |
|
|
192
|
+
|
|
193
|
+
See [`spec/anatomy-format.md`](anatomy-format.md) for constraints, cross-field validation, and the full anatomy part schema.
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
"anatomy": [
|
|
197
|
+
{ "name": "icon", "description": "Leading icon." },
|
|
198
|
+
{ "name": "label", "description": "Button text.", "required": true }
|
|
199
|
+
]
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## States (stub)
|
|
203
|
+
|
|
204
|
+
The `states` block declares the component's **interactive and semantic states** — the state terms used in token name-object `state` fields. Full normative definition is in [`spec/state-model.md`](state-model.md) (Phase 6.3).
|
|
205
|
+
|
|
206
|
+
**NORMATIVE:** `states` **MUST** be a JSON array. Each element is a state declaration object.
|
|
207
|
+
|
|
208
|
+
Each state carries at minimum:
|
|
209
|
+
|
|
210
|
+
| Field | Type | Required | Description |
|
|
211
|
+
| ------------ | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
212
|
+
| `name` | string | REQUIRED | State identifier (e.g. `hover`, `focus`, `disabled`). |
|
|
213
|
+
| `trigger` | string | OPTIONAL | `"prop"` for persistent prop-driven states (e.g. `isDisabled`) or `"interaction"` for runtime interaction states (hover, focus, pressed). |
|
|
214
|
+
| `precedence` | integer | OPTIONAL | Resolution precedence; higher integer wins when multiple states are active. |
|
|
215
|
+
| `layered` | boolean | OPTIONAL | `true` for states that compose with others (e.g. focus ring over hover). Default: `false`. |
|
|
216
|
+
|
|
217
|
+
See [`spec/state-model.md`](state-model.md) for the full state resolution algorithm, trigger semantics, and precedence rules.
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
"states": [
|
|
221
|
+
{ "name": "hover", "trigger": "interaction", "precedence": 50 },
|
|
222
|
+
{ "name": "focus", "trigger": "interaction", "precedence": 60, "layered": true },
|
|
223
|
+
{ "name": "disabled", "trigger": "prop", "precedence": 100 }
|
|
224
|
+
]
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Lifecycle
|
|
228
|
+
|
|
229
|
+
The `lifecycle` block tracks a component declaration's version history. It mirrors the per-token lifecycle pattern from [`spec/token-format.md`](token-format.md#lifecycle-and-metadata).
|
|
230
|
+
|
|
231
|
+
| Field | Type | Description |
|
|
232
|
+
| ------------------- | --------------- | ------------------------------------------------------------------------------ |
|
|
233
|
+
| `introduced` | string | Spec version when this component declaration was added (e.g. `"1.0.0-draft"`). |
|
|
234
|
+
| `deprecated` | string | Spec version when this component was deprecated. Truthy = deprecated. |
|
|
235
|
+
| `deprecatedComment` | string | Human-readable explanation of the deprecation and migration path. |
|
|
236
|
+
| `replacedBy` | string or array | `name` value(s) of the replacement component(s). |
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
"lifecycle": {
|
|
240
|
+
"introduced": "1.0.0-draft"
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## SPEC rules
|
|
245
|
+
|
|
246
|
+
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).
|
|
247
|
+
|
|
248
|
+
| Rule ID | Name | Severity | Assert |
|
|
249
|
+
| -------- | --------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
250
|
+
| SPEC-018 | `component-name-exists` | error | Token `component` field value **MUST** match the `name` of a declared component in the dataset. |
|
|
251
|
+
| 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). |
|
|
252
|
+
| SPEC-020 | `component-anatomy-valid` | error | Token `anatomy` field value **MUST** match the `name` of a declared anatomy part on the referenced component. |
|
|
253
|
+
| 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. |
|
|
254
|
+
| 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). |
|
|
255
|
+
|
|
256
|
+
## Full example
|
|
257
|
+
|
|
258
|
+
A complete button component declaration:
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/component.schema.json",
|
|
263
|
+
"$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/button.json",
|
|
264
|
+
"specVersion": "1.0.0-draft",
|
|
265
|
+
"name": "button",
|
|
266
|
+
"displayName": "Button",
|
|
267
|
+
"description": "Buttons allow users to perform an action or to navigate to another page.",
|
|
268
|
+
"meta": {
|
|
269
|
+
"category": "actions",
|
|
270
|
+
"documentationUrl": "https://spectrum.adobe.com/page/button/"
|
|
271
|
+
},
|
|
272
|
+
"options": {
|
|
273
|
+
"variant": {
|
|
274
|
+
"type": "string",
|
|
275
|
+
"enum": ["accent", "negative", "primary", "secondary"],
|
|
276
|
+
"default": "accent",
|
|
277
|
+
"description": "Visual emphasis level."
|
|
278
|
+
},
|
|
279
|
+
"style": {
|
|
280
|
+
"type": "string",
|
|
281
|
+
"enum": ["fill", "outline"],
|
|
282
|
+
"default": "fill"
|
|
283
|
+
},
|
|
284
|
+
"size": {
|
|
285
|
+
"type": "string",
|
|
286
|
+
"enum": ["s", "m", "l", "xl"],
|
|
287
|
+
"default": "m"
|
|
288
|
+
},
|
|
289
|
+
"isDisabled": { "type": "boolean", "default": false },
|
|
290
|
+
"isPending": { "type": "boolean", "default": false },
|
|
291
|
+
"isLabelHidden": { "type": "boolean", "default": false },
|
|
292
|
+
"icon": {
|
|
293
|
+
"$ref": "https://opensource.adobe.com/spectrum-design-data/schemas/types/workflow-icon.json",
|
|
294
|
+
"description": "Icon placed at the start of the button. Required when isLabelHidden is true."
|
|
295
|
+
},
|
|
296
|
+
"staticColor": {
|
|
297
|
+
"type": "string",
|
|
298
|
+
"enum": ["white", "black"],
|
|
299
|
+
"description": "Static color for use on colored backgrounds. Must not be set for the default variant."
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
"slots": [
|
|
303
|
+
{
|
|
304
|
+
"name": "default",
|
|
305
|
+
"description": "Text label of the button.",
|
|
306
|
+
"required": false
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"name": "icon",
|
|
310
|
+
"description": "Icon placed at the start of the button."
|
|
311
|
+
}
|
|
312
|
+
],
|
|
313
|
+
"anatomy": [
|
|
314
|
+
{ "name": "icon", "description": "Leading icon." },
|
|
315
|
+
{ "name": "label", "description": "Button text.", "required": true }
|
|
316
|
+
],
|
|
317
|
+
"states": [
|
|
318
|
+
{ "name": "hover", "trigger": "interaction", "precedence": 50 },
|
|
319
|
+
{ "name": "focus", "trigger": "interaction", "precedence": 60, "layered": true },
|
|
320
|
+
{ "name": "disabled", "trigger": "prop", "precedence": 100 }
|
|
321
|
+
],
|
|
322
|
+
"lifecycle": {
|
|
323
|
+
"introduced": "1.0.0-draft"
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
package/spec/evolution.md
CHANGED
|
@@ -84,7 +84,6 @@ The `@adobe/spectrum-tokens` package continues to publish tokens in the **legacy
|
|
|
84
84
|
| `deprecated: "3.2.0"` (version string) | `deprecated: true` (boolean) |
|
|
85
85
|
| `replaced_by: "<uuid>"` | `renamed: "<target-token-name>"` |
|
|
86
86
|
| `introduced` | Not emitted |
|
|
87
|
-
| `lastModified` | Not emitted |
|
|
88
87
|
| `plannedRemoval` | Not emitted |
|
|
89
88
|
| `deprecated_comment` | `deprecated_comment` (passed through) |
|
|
90
89
|
|