Skip to content

ADRs 042, 047–049: Composition, Slot Content, and Ready-Made Examples#91

Open
nathanacurtis wants to merge 22 commits into
mainfrom
042-composition-type
Open

ADRs 042, 047–049: Composition, Slot Content, and Ready-Made Examples#91
nathanacurtis wants to merge 22 commits into
mainfrom
042-composition-type

Conversation

@nathanacurtis
Copy link
Copy Markdown
Member

No description provided.

nathanacurtis and others added 22 commits April 29, 2026 07:35
Introduces Composition as a peer type to Component, covering slot
defaults, component examples, layout patterns, and page views.
Supersedes draft ADR-025 (nested slot API).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Component.examples: Record<string, Composition> (inline, kind-tagged)
- CompositionRef { $composition: string } in Children union for slot bindings
- Slot defaults live in variant/element layer, vary per variant
- PropConfigurations widening retained (consumer inline pattern)
- System-scoped compositions.yaml schema deferred to follow-on ADR

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…faults

- Rename CompositionRef → CompositionReference (then remove: not needed)
- Remove CompositionReference from Children union — Children unchanged
- Slot default composition lives in Element.$extensions['com.figma']
  following established provenance-metadata pattern on Props/TokenReference
- Add ElementExtensions + FigmaElementExtension types on Element
- Slot defaults are Figma provenance, not public component API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
defaultComposition is meaningful only when Element.children is a
PropBinding (slot-bound container / Figma SlotNode). Container elements
with string[] children are FrameNodes and must never carry it.
No new element type introduced.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ComponentExample = SlotExample | InstanceExample (kind-discriminated)
- SlotExample: kind+'slot'+anatomy+elements+layout — no inline nesting
- InstanceExample: kind+propConfigurations (scalars)+slots (named refs)
- Composition retained as structural base for system-scoped follow-on
- PropConfigurations widens to PropBinding only; inline Composition deferred
- Remove CompositionKind; layout/page kinds moved to follow-on ADR
- All examples use aSlotProperty/aSlotElement naming

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ciple to ADR 042

- Replace Card/ProductCard examples with ActionList/ActionListItem throughout
- Add sensitizing example section: illustrates 56 to 16 entry reduction via
  one-level-deep principle and no cross-component references
- Add two new Decision Drivers: One level deep and No cross-component references
- Update Option A Pros to call out scale reduction explicitly
- Update Notes to explain the ActionListItem boundary and why it is intentional

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ADR-042 now covers only the Composition base type (anatomy, elements, layout).
The four-ADR breakdown:
  043 — Component Examples: InstanceExample + Component.examples
  044 — Slot Content: SlotExample + Element extensions
  045 — PropConfigurations PropBinding

Example anchors on ActionListItem with state/title/description scalar props;
slots are introduced in ADR-044.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…opBinding

043: InstanceExample + ComponentExamples + Component.examples
     ActionListItem anchors the example with state/title/description scalar props
     InstanceExample.slots forward-compatible; meaningful once 044 lands

044: SlotExample + ElementExtensions + Element.$extensions
     ActionListItem startVisual/endVisual slots; ActionList items slot one level deep
     16 entries on ActionList (not 56); one-level-deep principle; no cross-component refs
     Widens ComponentExamples to ComponentExample = InstanceExample | SlotExample

045: Widen PropConfigurations to include PropBinding
     Completes binding pattern already on Element.content, instanceOf, Styles.visible

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…xample

- Replace thin two-option section with four substantive options exploring:
  - Anatomy/elements convergence (B — rejected: breaks SlotExample inheritance)
  - All three required (A — selected)
  - elements optional (C — rejected: anatomy-only is degenerate)
  - layout optional (D — rejected: implied order is invisible convention)
- Fix example: ActionListItem with root/label/description, not a single 'item'
- Add description? field alongside title?
- Note nested-instance-with-slot constraint explicitly in Notes
- Note anticipated metadata extensions (tags, deprecated, guidelines)
- Propagate required elements+layout to ADR-044 SlotExample

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 2x page grid padding and nav–content gutter at wide viewports
- Increase sidebar item spacing (+4px) and padding (+2px → 8px)
- Remove distinct surface colors and borders between nav/sidebar/content
- Force dark mode and hide theme toggle
- Move GitHub and Figma Plugin links from header to top of sidebar

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lotBinding

Substantial revisions following review:

- Drop SlotExample type; reuse Composition directly
- Drop the per-entry `slot` field — content entries are slot-agnostic
- Make slot content a sibling field (`Component.slotContent`) rather than a
  discriminated-union peer of `InstanceExample` in `Component.examples`
- Move Figma default-fill reference into `SlotBinding.$extensions['com.figma'].default`
  (inside `Element.children`, not a sibling on `Element`) to reflect that it is
  design-tool authoring provenance that code consumers must correctly ignore
- Defer recursion (nested slot filling) and its motivating example to a
  follow-on ADR; keep this ADR scoped to one level deep
- Add rejected options for Element-sibling field, content widening
- Tighten Context and trim Option A example to the minimum needed to show
  both reference sites

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ionale

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ompositions

main owns ADR-043 (custom-color-format-config) and PR #60 owns ADR-044/045
(duplicate-layer-disambiguation, processing-provenance-signals). This branch's
three ADRs collide with both. Move ours to the next free range so PR #60 and
already-merged work remain valid:

- 043-component-examples           -> 046-component-examples
- 044-slot-content                 -> 047-slot-content
- 045-prop-configurations-binding  -> 048-prop-configurations-binding

Reserve 049 for the deferred recursion ADR (Nested Slot Compositions, called
out as a follow-on in ADR-047). Stub authored.

Updates all ADR-NNN cross-references and branch-name references inside our
four ADRs and INDEX.md. INDEX.md description for the slot-content row also
brought in line with the redesigned ADR-047 title (the prior description
referenced removed concepts like SlotExample / defaultComposition).

Indexing change only - no design decisions altered.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…+cli-0.14.0

# Conflicts:
#	adr/INDEX.md
#	packages/cli/CHANGELOG.md
#	packages/cli/package.json
…type

# Conflicts:
#	adr/042-composition-type.md
#	adr/INDEX.md
Slot fill is unified under Element.propConfigurations per ADR-049, so
InstanceExample.slots becomes redundant. With slot fills handled
separately, Component.examples only ever holds InstanceExample entries
and the kind discriminator carries no information.

Updates Decision Drivers, Options A and B, Notes, Consequences, schema
definition, and parity check to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…search scratchpads

ADR-049 now compares four shapes for cross-boundary slot fill:
  A. Nested only (inline Composition under propConfigurations)
  B. Flat only (key reference, discriminated as { \$slotContent: <key> })
  C. Both forms accepted
  D. Sibling field (Element.slotFills)

Key decisions captured:
- Recursion depth is unbounded (Decision Driver, not Out of Scope)
- Cycle detection is a constraint, enforced by consumer validators
- Layout direction lives in styles.layoutMode, not at the slot boundary
- Named compositions can live in Component.slotContent (component-scoped)
  OR in 1+ external composition files (system-scoped, ADR-042 follow-on)
- Discriminated reference form { \$slotContent: <key> } parallels PropBinding

Scratchpads in adr/research/049/ demonstrate the design space concretely
through a deeply-nested filter results page (Page > Row > Accordion >
CheckboxGroup > Checkbox > custom-children) in both nested and flat
forms. The flat form is system-scoped (spans multiple components).

ADR-047 simplifications (drop \$extensions / SlotBinding) and field-naming
questions (slotContent vs compositions; \$slotContent vs \$composition)
flagged for follow-up rather than rolled in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mples; add Pill scratchpad

Three coordinated changes across the composition ADRs:

1. Reference form is JSON Pointer, paralleling ADR-008 PropBinding.
   - ADR-049's discriminated reference becomes { $composition: <pointer> }
     (rotated from $slotContent; pointer makes the registry home explicit).
   - ADR-047's SlotBinding.$extensions['com.figma'].default value becomes
     a JSON Pointer to a Composition (was a bare slotContent key).
   - Scratchpads updated: example.flat.yaml wraps records under a top-level
     `compositions:` namespace so absolute pointers resolve; example.pill.yaml
     (new) wraps under `components: pill:` for the same reason.

2. Rename ComponentExamples → InstanceExamples; Component.examples →
   Component.instanceExamples. Disambiguates against Props.examples and
   Anatomy.examples (which carry sample content, not InstanceExample
   entries). Cited in ADR-046 Semver section as the first ADR-side
   application of Constitution §VI rule 3 (no code-platform consensus
   on this specs-schema-specific authoring construct).

3. New cross-ADR scratchpad example.pill.yaml — a Pill component that
   exercises ADR-008 (PropBinding), ADR-046 (instanceExamples),
   ADR-047 (slotContent + SlotBinding with Figma authoring default),
   and ADR-049 (discriminated $composition reference) in one file.
   Three documented usages span the surface: configuredLabel
   (custom:false + label scalar), composedLabel (custom:true filled
   with Figma's default composition), removableLabel (custom:true
   filled with text + remove glyph composition).

Cleanup along the way: ADR-047 had stale references to the removed
InstanceExample.slots field (six sites in Notes / Pros / Consequences /
Out of scope / Option D rejection). All updated to point at the unified
propConfigurations.<slotName> mechanism per ADR-049. The Option A example
in ADR-047 was wrapped under `components: actionListItem:` and updated
to JSON Pointer references, replacing the pre-rename `examples.slots`
form with `instanceExamples.propConfigurations.<slot>: { $composition }`.

ADR-042 forward references (lines 25, 195, 206) updated from
Component.examples to Component.instanceExamples.

INDEX.md row 046 updated to reflect the new title and field name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nathanacurtis nathanacurtis changed the title 042 composition type ADRs 042, 047–049: Composition, Slot Content, and Ready-Made Examples May 12, 2026
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