Repository navigation
feat(visuals): bind badge, button and action-button recipes through relationships (2b/6) - #1577
Conversation
A relationship's scope.options can now pin component options (arrays mean any of). For a relationship selector the renderer keeps the records whose options match the request and prefers the one that pins the most; a record with no options is the default. The JS runtime applies the same step. The schema, SPEC-053 and SPEC-066 follow. Badge moves over: its recipe shrinks from 1896 to 289 lines and its token selectors from 145 to 0, with all 9000 render combinations byte-identical. Also adds a render snapshot test for every visual recipe (one hash per option set, 53,544 combinations) and a SceneCache that makes matrix renders about 70x faster.
…o relationships Padding, gap, stroke width, corner radius and font size now bind through relationships (7 selectors each); all 39,936 render combinations are byte-identical. 16 relationship records that pointed twice at the desktop token now point at the mobile token, so the mobile renders resolve. The recipe lint baseline drops to match.
…ts token A relationship that binds a plain palette token (no state in its name) can now say which state it applies to with scope.options.state, so a recipe can bind a stateful color through a relationship. The compiled candidates carry the merged states, so the JS runtime needs no change. No existing recipe output changes.
…on-scoped relationships Fill, stroke, icon, label and hold-icon colours move from token selectors in the recipes to relationship records pinned by scope.options (and a state where needed). A pinned relationship state now replaces the token's own state, and 'focus' reads as keyboard-focus. Rendered output is unchanged across every option combination.
…or everywhere Design settled on focus-indicator (it names the intent, not the shape). The focus-ring gap token, anatomy term and the button and action-button anatomy and recipe parts now use it; the token keeps legacyKey focus-ring-gap so legacy output is unchanged. focus ring stays as the name of the shared ring style class in the terminology.
…h relationships The recipe used the deprecated transparent-white/black-1000 tokens. The Static white and Static black focus indicator contexts hold tokens with the same values, so the rendered stroke is unchanged; only the token label in the scene changes.
…ugh relationships New Progress and Progress track contexts reference the same gray and accent tokens the recipes selected before, so renders are unchanged.
|
| Name | Type |
|---|---|
| @adobe/spectrum-design-data | Patch |
| @adobe/design-data-spec | Minor |
| @adobe/design-data-wasm | Minor |
| @adobe/spectrum-component-api-schemas | Patch |
| @adobe/design-system-registry | Patch |
| @adobe/design-data-agent-mcp | Patch |
| @adobe/design-data-mcp | Patch |
| markdown-generator | Patch |
| @adobe/token-corpus-migrate | Patch |
| @adobe/token-naming-audit | Patch |
| @adobe/design-data | Patch |
| s2-tokens-viewer | Patch |
| @adobe/spectrum-design-data-mcp | Patch |
| component-options-editor | Patch |
Click here to learn what changesets are, and how to add one.
Click here if you're a maintainer who wants to add a changeset to this PR
Run report for 8f5c9850Total time: 7m 8s | Comparison time: 14m 51s | Estimated savings: 7m 42s (51.9% faster)
Expanded report
Changed files |
🧩 Component Schema Changes ReportComponent Schemas Changed (0 added, 0 deleted, 2 updated)Original Branch: ✅ No Breaking ChangesThis PR contains only non-breaking changes to component schemas. 🔄 Non-Breaking Updatesaction-button button This diff was generated automatically and contains only backward-compatible changes. This comment was automatically generated by the component schema diff tool. 🤖 |
🎨 Token Changes ReportTokens Changed (0)Original Branch: This comment was automatically generated by the token diff tool. 🤖 |
Squash merge is fine; the next slice is rebased onto the result.
Part 2b of 6 of the visual recipes work from draft #1569, targeting the long-lived integration branch
next/visual-recipes(notmain). Based on #1576 (slice 2a).Description
Recipes now take their values from the component's relationships instead of naming tokens directly. This slice moves badge, button and action-button over, and adds what the move needs.
scope.optionscan pin component options (an array means any of). For a relationship selector the renderer keeps the records whose options match the request and prefers the one that pins the most; a record with no options is the default. The JS runtime applies the same step. The schema, SPEC-053 and SPEC-066 follow.scope.options.statecounts as a state of the token it binds, so a plain palette token can be bound for one state. The final semantics: a pinnedscope.options.statereplaces the bound token's ownname.state, andfocusandkey-focusboth read askeyboard-focus.sdk/core/tests/relationship_states.rspins this (the_relationship_state_picks_the_hover_token,compiled_candidates_carry_the_relationship_state,a_pinned_state_replaces_the_tokens_own_state).focus-ringanatomy part, gap token term and recipe parts are nowfocus-indicator. The token keepslegacyKeyfocus-ring-gap, so legacy output is unchanged.sdk/core/tests/recipe_snapshots.rsrenders every valid option combination of every recipe (53,544) across sizes, states, color schemes and scales, and hashes the SVG and diagnostics per option set intofixtures/recipe-snapshots.json. ASceneCachemakes matrix renders about 70x faster.$reffrom the desktop token to the same-named mobile token (the fix fix(design-data): point the second duplicate relationship at the mobile token #1572 made for 17 other components): badge 8, button 8, action-button 8. Counted by script, comparingrelationships/{badge,button,action-button}.jsonbefore (the 2a tree) and after for records with the same scope, context and options; all 24 changes are desktop to mobile with the same token name, and none is any other kind of change. Records the generator added, removed or re-scoped have no counterpart to compare and are not counted.Not in this slice: site UI polish and WASM parity tests (slice 3), anatomy standard (4), platform-aware rendering (5).
The four action-button selectors that stay are values no relationship covers yet (SPEC-067 keeps reporting them).
How to review this
The relationship JSON cannot be reviewed line by line. It adds about 5,650 lines across
relationships/{badge,button,action-button}.json(roughly 260 new records, most of them pinned byscope.options). Review the mechanism and the evidence instead.How the records were made: they are generated, not hand-written, by throwaway scripts that are not committed. The method:
scope.options(andstatewhere needed), reusing existing contexts or adding role-named ones (Label, Progress, Progress track, Hold icon).Correctness rests on the equivalence evidence and the snapshot test below, not on the generator.
Evidence that the move changes no output:
recipe_snapshots.rsand its fixture are in this PR, so a reviewer can regenerate and compare:UPDATE_RECIPE_SNAPSHOTS=1 cargo test -p design-data-core --test recipe_snapshots.data-partlabel changed fromfocus-ringtofocus-indicator(the rename above).design-data render, old data against new. After mappingdata-part="focus-ring"tofocus-indicator, all are identical, and the sets that differ before that mapping are exactly the focus-state ones (81 button, 48 action-button). This is a sample, not the full sweep; the full sweep is what the snapshot test covers.Review guide, in order:
spec/component-visuals.md,component-visual.schema.jsonand SPEC-053/066: howscope.optionsis specified.sdk/core/src/visual.rs(selection step) and the matching JS runtime change: the preference rule.sdk/core/tests/relationship_states.rs,recipe_snapshots.rsand therecipe_lint_baselinechanges.visuals/: shorter, with relationship selectors (git diffshows the token selectors removed).relationships/*.json; spot-check a few records withdesign-data render <component> --option ....Related Issue
Epic spectrum-design-data-p6ga (component visual recipes); slicing tracked in p6ga.9; split from #1569. Part 2b of 6, after #1576. Decision recorded in the bead notes: visual recipes bind values through relationships by default.
Motivation and Context
A recipe that names tokens duplicates what the relationships already say and drifts from them. Binding through relationships keeps one source of truth, and shrinks the recipes (badge by about 85%), which also makes the recipe lints (SPEC-067) pass for the migrated bindings.
How the slice was made: the 10 commits of #1569 that follow slice 2a, cherry-picked in order and then rebased onto the merged 2a. Commits already on
main(5f51c2b4, the 103 repoints;5c38e349, SPEC-072) were skipped, so the repoint and SPEC-072 changeset text those commits touched is unchanged here. There were no conflicts.How Has This Been Tested?
On the slice head, in a clean worktree, rebased onto the merged 2a:
cargo fmt --checkandcargo clippy --workspace --all-targets -- -D warningsmoon run sdk:test sdk-wasm:test design-data:validate design-data:validate-dataset tokens:verifyLegacyOutput --force(uncached): all 11 tasks pass; legacy output unchangedrecipe_snapshotsandrecipe_lint_baselinepass with the committed fixturesnode tools/changeset-linter/src/cli.js check --fail-on-warnings: 7 validnode .github/scripts/check-ci-coverage.mjs: 0 uncoveredpnpm ai:checkvalid;git diff pnpm-lock.yamlemptyScreenshots (if appropriate):
N/A
Types of changes
Changeset:
checkbox-relationship-context-prefixis extended to cover this slice. It accumulates on the integration branch and is not released until the final merge tomain.Checklist: