Skip to content

feat(core): expand {option} placeholders in visual recipe selectors - #1586

Merged
GarthDB merged 4 commits into
mainfrom
feat/recipe-selector-templating
Nov 1, 2026
Merged

GarthDB merged 4 commits into
mainfrom
feat/recipe-selector-templating

Conversation

@GarthDB

@GarthDB GarthDB commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

Description

Recipe token and relationship selectors can now use {<option>} for any option the component declares, such as {style}. {size} and {sizeIndex} work as before.

  • A selector that names an option compiles once per declared value. Each candidate pins that option, so the existing solver and visual-runtime.js pick by the request and need no change.
  • SPEC-061 reports a {name} placeholder that is not size, sizeIndex or a declared option that lists its values. SPEC-062, 066, 067, 069 and 071 expand the placeholders before resolving, and SPEC-066 counts an option used only through a placeholder as used.
  • A brace pair whose content is not a plain identifier stays literal text. No existing recipe has one.
  • Spec text in component-visuals.md, the rules.yaml notes and the schema description are updated. The invalid/SPEC-061 conformance fixture covers the new check.

This departs from proposal 014, which said to ship the first iOS Button slice with explicit per-style entries and template before a third style. Measured with the proposal's recipe shape, explicit entries cost 144, 197 and 250 lines for one, two and three styles (515 for eight). The templated recipe is 128 lines for any number of styles. Templating is already shorter at one style, so writing two explicit styles and then rewriting them is wasted work. The proposal has a dated note.

Output for recipes that use no option placeholder is unchanged: recipe_snapshots and every committed golden are untouched.

Related Issue

p6ga.8.46, part of epic p6ga.8 (platform-aware rendering). It unblocks the iOS Button recipe in p6ga.8.40.

Motivation and Context

The iOS Button binds tokens directly by name, per style. Without option templating, every added style adds about 53 lines to the recipe, and eight styles reach 515. With it, adding a style is a token and an option value.

Risks

An unknown identifier-like {name} in a recipe selector used to pass silently as literal text. It is now a SPEC-061 error, and the message names the selector path. No committed recipe is affected: the only placeholders in packages/design-data/visuals and platforms are {size} and {sizeIndex}, and the existing validation and snapshot suites pass unchanged. A recipe outside this repo that has such a brace would need to rename it or add the option.

How Has This Been Tested?

  • Unit tests for placeholder scanning, option expansion in the compiled recipe (candidates pin the option, the default follows the option default), SPEC-061 (unknown option, option without values; size, sizeIndex and non-identifier braces are fine) and SPEC-062 (expansion over declared values).
  • SPEC-066: an option used only through a {option} placeholder is not reported as unused. Compile: a relationship that pins an option to an array of values still matches under a placeholder.
  • invalid/SPEC-061 conformance fixture with a second expected error.
  • JS runtime: sdk/wasm/test/visual-runtime-option-templates.test.js renders a compiled {style} recipe through visual-runtime.js and asserts that accent, primary and the default select different candidates. Its compiled fixture is the compile_recipe output of the Rust test, pasted in, so it can drift from the compiler. The existing parity test does not cover an option-templated recipe, because no committed recipe uses one yet.
  • cargo fmt --check and clippy -D warnings clean; cargo check after each commit.
  • moon run sdk:test sdk-wasm:test design-data:validate design-data:validate-dataset tokens:verifyLegacyOutput root:validate-platform-recipes root:test-platform-recipes-guard root:test-platform-recipes-e2e --force: all pass (1733 SDK tests, 121 WASM tests).
  • Changeset linter with --fail-on-warnings, check-ci-coverage and ai:check pass; pnpm-lock.yaml diff is empty.

Screenshots (if appropriate):

N/A

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • I have signed the Adobe Open Source CLA.
  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

test added 3 commits October 31, 2026 22:25
A token or relationship selector can name any declared option, for example {style}. It compiles once per declared value and each candidate pins that option, so the solver and the JS runtime need no change. SPEC-061 reports a placeholder that is not size, sizeIndex or a declared option with values. {size} and {sizeIndex} behave as before.
@changeset-bot

changeset-bot Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f3f68a8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@adobe/design-data-spec Minor
@adobe/design-data-tui Minor
@adobe/design-data-wasm Minor
@adobe/design-data Patch
s2-tokens-viewer Patch
@adobe/design-data-agent-mcp Patch
@adobe/design-data-mcp Patch
@adobe/spectrum-design-data-mcp Patch
@adobe/token-naming-audit Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Run report for f3f68a8e

Total time: 6m 13s | Comparison time: 18m 32s | Estimated savings: 12m 19s (66.4% faster)

Action Time Status Info
⬛️ SetupProto(0.57.4) 0ms Skipped
🟩 SyncWorkspace 9.4ms Passed
🟩 SyncProject(sdk) 0.4ms Passed
🟩 SyncProject(root) 1ms Passed
🟩 SyncProject(sdk-wasm) 1ms Passed
⬛️ SetupToolchain(node:20.17.0) 466.2ms Skipped
⬛️ SetupToolchain(rust:1.88.0) 787.3ms Skipped
🟩 SetupEnvironment(rust, sdk) 72ms Passed
⬛️ InstallDependencies(rust, sdk) 2.2ms Skipped
⬛️ SetupToolchain(pnpm:10.17.1) 645ms Skipped
⬛️ SetupToolchain(javascript) 0ms Skipped
🟩 SetupEnvironment(javascript) 0.7ms Passed
⬛️ SetupEnvironment(javascript, sdk) 0.9ms Skipped
⬛️ InstallDependencies(javascript) 0.3ms Skipped
🟩 RunTask(sdk:fmt-check) 591.8ms Passed
🟩 InstallDependencies(javascript, sdk) 3s Passed
🟩 RunTask(sdk:codegen-check) 65.2ms Passed
🟩 RunTask(sdk:lint) 40s Passed
🟩 RunTask(sdk:build) 1m 13s Passed
🟩 RunTask(sdk:test-doc) 1m 33s Passed
And 7 more...
Expanded report
Action Time Status Info
🟩 RunTask(sdk-wasm:cache-build) 2m Passed SLOW
🟩 RunTask(sdk:test) 4m 44s Passed SLOW
🟩 RunTask(sdk-wasm:site-data) 3m 34s Passed SLOW
🟩 RunTask(sdk-wasm:build) 3m 12s Passed SLOW
🟩 RunTask(sdk-wasm:test) 8.5s Passed
🟩 RunTask(sdk-wasm:ci) 10.4ms Passed
🟩 RunTask(root:test-platform-recipes-e2e) 4m 35s Passed SLOW
Changed files
.changeset/recipe-selector-option-templates.md
docs/proposals/014-platform-owned-visual-recipes.md
packages/design-data-spec/conformance/invalid/SPEC-061/dataset.json
packages/design-data-spec/conformance/invalid/SPEC-061/expected-errors.json
packages/design-data-spec/rules/rules.yaml
packages/design-data-spec/schemas/component-visual.schema.json
packages/design-data-spec/spec/component-visuals.md
sdk/core/src/validate/rules/spec061.rs
sdk/core/src/validate/rules/spec062.rs
sdk/core/src/validate/rules/spec066.rs
sdk/core/src/validate/rules/spec067.rs
sdk/core/src/validate/rules/spec069.rs
sdk/core/src/validate/rules/spec071.rs
sdk/core/src/validate/rules/visual_util.rs
sdk/core/src/visual.rs
sdk/wasm/test/visual-runtime-option-templates.test.js

@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

🎨 Token Changes Report

Tokens Changed (0)

Original Branch: main


This comment was automatically generated by the token diff tool. 🤖

@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

🧩 Component Schema Changes Report

No component schema changes detected.

This comment was automatically generated by the component schema diff tool. 🤖

…y pins

A recipe that uses an option only through a {option} selector placeholder no longer
gets a SPEC-066 warning. A relationship that pins an option to an array of values now
matches under a placeholder when the array contains the value. The no-match message is
built without a second graph scan.
@GarthDB
GarthDB merged commit 706b12e into main Nov 1, 2026
9 checks passed
@GarthDB
GarthDB deleted the feat/recipe-selector-templating branch November 1, 2026 05:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant