ADR
Component Slot Examples
Context
ADR-042 established Composition as the named structural unit. ADR-046 established SlotContent as the anonymous structural triplet, slotContent on Composition for bundled fills, and SlotContentRef as the universal $slotContent pointer.
This ADR addresses the component side of slot content: two authoring needs arise from Figma’s slot model.
-
Where do named slot fill examples live on
Component? A component designer authors named, reusable fills for a component’s slot props — for example, an icon that fillsActionListItem’sstartVisualslot by default. These fills are examples and test cases; they carry no implementation obligation for code consumers. They need a named home onComponent, sibling to whatever field holds instance examples (ADR-048). -
How does Figma’s default slot fill get expressed in the spec? A slot-bound container in
default.elementsorvariants[n].elementsbindschildrento a slot prop viaPropBinding. In the Figma file, that slot layer has content placed inside it — the design-tool default. That default is Figma authoring provenance: code components resolve missing slots through logic, not data, so code consumers must be able to correctly ignore it. The reference must be colocated with the slot binding it documents and carried under a field whose name marks it as non-contractual sample content.
The existing Element.children type (string[] | PropBinding — plain-frame children vs. slot-bound binding) is extended here; no new element type is introduced.
Decision Drivers
- Examples, not contracts —
slotContentExamplesentries carry no implementation obligation for code consumers; they are authored examples and test cases, parallel in intent toinstanceExamples(ADR-048). The-Examplessuffix signals this correctly. - Slot-agnostic entries — the same fill (e.g., a glyph icon) may fill
startVisualorendVisual; binding content to a specific slot at the entry site forces duplication. Binding happens at the reference site. - Separation from instance examples —
InstanceExampledocuments a whole-component usage configured via scalar props; slot content documents fill for a named slot. Different purposes, different audiences, different reference patterns. A flat sibling field onComponentmakes the split explicit. - Figma default is design-tool provenance — Figma stores what’s placed inside a slot layer as part of the design file. Code consumers have no “default slot data” concept; defaults in code are logic. The reference must live under a field that code consumers correctly treat as non-contractual — the
examplesconvention (already used onStringProp.examples,NumberProp.examples, and at the component level onslotContentExamples/instanceExamples) is that mechanism. - Reuse the
examplesconvention —StringProp.examples: string[]andNumberProp.examples: number[]already hold authored sample values for typed props, andStringProp.defaultis deprecated in favor ofexamples.SlotBinding.examples: SlotContentRef[]is the structural-fill analogue: one consistent name for “authored, non-contractual sample content” acrossProps,SlotBinding, andComponent. - Default colocated with binding — the slot binding and its Figma default describe the same object; they live on the same
SlotBinding, not separated across fields. PropBindingstays narrow —PropBindingis used bycontent,instanceOf, andvisibleas well aschildren. Slot example fills are meaningful only for slot-boundchildren;exampleslives on achildren-specific subtype (SlotBinding), not onPropBindingitself.- Variant-sensitive defaults for free —
Element.childrenlives indefault.elementsandvariants[n].elements; per-variant Figma defaults fall out without any new mechanism. - Additive-only — all new fields are optional; no existing type narrows → MINOR
- Type ↔ schema symmetry — Constitution §I
- No runtime logic — Constitution §II
Options Considered
Option A: Component.slotContentExamples: Record<string, SlotContent> + SlotBinding.examples: SlotContentRef[] (Selected)
A new top-level field Component.slotContentExamples holds named SlotContent entries — sibling to Component.instanceExamples (ADR-048). SlotContent is defined in ADR-046.
A new interface SlotBinding extends PropBinding with an optional examples field: an array of SlotContentRef (ADR-046’s universal { $slotContent: <pointer> } discriminated reference). Children widens from string[] | PropBinding to string[] | SlotBinding. For now, examples always carries a single entry at index 0 — Figma’s default fill for the slot layer — but the array shape leaves room for additional authored example fills in the future without further schema change.
This generalizes the existing examples convention already established for typed props in Props.ts: StringProp.examples: string[] and NumberProp.examples: number[] hold sample values demonstrating typical content for the prop, and StringProp.default is already deprecated (@deprecated Use examples for demo content) in favor of examples. The same convention here scales from scalar prop values to structural slot fills: SlotBinding.examples: SlotContentRef[] is the slot-binding analogue.
# ActionListItem — slotContentExamples and SlotBinding togethercomponents: actionListItem: props: startVisual: { type: slot }
slotContentExamples: searchIcon: anatomy: icon: { type: glyph } elements: icon: content: search styles: { width: 16, height: 16 } layout: [icon]
default: elements: startVisualSlot: children: $binding: "#/components/actionListItem/props/startVisual" examples: - $slotContent: "#/components/actionListItem/slotContentExamples/searchIcon"Each examples[i] resolves using the same rules as any SlotContentRef (ADR-046): the $slotContent value is a JSON Pointer to a SlotContent in slotContentExamples or to a Composition in an external compositions file.
Naming slotContentExamples
slotContentExamples(Selected) — mirrorsinstanceExamplesin suffix and semantics. Entries are examples and test cases; code consumers are not obligated by them. Figma references them as authoring defaults, but the binding lives onSlotBinding— the entries themselves are examples Figma happens to reference. “Content” qualifies the kind of example (slot fill, not whole-component usage).slotContent— drops the-Examplessuffix, implying a stronger contract than these entries carry.slotExamples— reads as “examples of slots” rather than “examples of content for slots.”slotCompositions— accurate to the structural shape but not everyday authoring vocabulary.slots— collides withSlotPropusage onComponent.props.fills/slotFills— jargony; no precedent in target platform vocabularies.
Naming examples
examples— mirrors the existing-Examplesconvention already established byslotContentExamplesandinstanceExamples(ADR-048): a field that holds authored, non-contractual reference material. Code consumers that understand theexamplesconvention skip it the same way they skipslotContentExamplesandinstanceExamplesat the component level.exampleContent/exampleFills— more descriptive but introduce a one-off term whereexamplesalready carries the right meaning in context.$extensions['com.figma'].default— see Option B; trades the consistentexamplesconvention for explicit design-tool-provenance framing.
Pros:
- Reuses the existing
examplesconvention already in use onStringPropandNumberProp(wheredefaultis deprecated in its favor) — single mental model for “authored, non-contractual sample content” acrossProps(scalarexamples),SlotBinding(structuralexamples), andComponent(slotContentExamples,instanceExamples). - Uses the universal
SlotContentRef($slotContent) from ADR-046 — same reference shape thatpropConfigurations.<slotName>(ADR-049) uses; one resolver covers all slot-content reference sites. - Array shape is future-proof — additional authored example fills can be added without widening the schema again (current emitter writes only index 0).
- No DTCG
$extensionsnamespacing required for what is, conceptually, an example rather than platform metadata. SlotBinding extends PropBinding— existingchildren: { $binding }values still validate;examplesis optional. Per-variant Figma defaults still fall out for free.
Cons / Trade-offs:
- Loses the explicit “design-tool provenance” signal that
$extensions['com.figma']carries.examplesis a neutral name; the fact that Figma populates index 0 with the slot layer’s authored content is a producer convention, not a schema-level guarantee. - Two sibling fields on
Component(slotContentExamples,instanceExamples) — unchanged from Option B. - Schema cannot enforce that
$slotContentpointers inexamplesresolve — consumer validation concern (same as Option B). - Array-with-always-one-item carries minor structural overhead today for capacity that only matters later.
Option B: Component.slotContentExamples: Record<string, SlotContent> + SlotBinding.$extensions['com.figma'].default (Rejected)
Same Component.slotContentExamples shape, but SlotBinding carries the Figma authoring default at $extensions['com.figma'].default (a plain JSON Pointer string) instead of in an examples array.
Rejected because: the examples convention is already established in the schema — StringProp.examples: string[] and NumberProp.examples: number[] hold sample/demo content, and StringProp.default is marked @deprecated Use examples for demo content. Reusing examples at the slot-binding site keeps “authored, non-contractual sample content” under one consistent name across Props, SlotBinding, and Component. The $extensions['com.figma'] framing carried stronger design-tool-provenance signaling, but at the cost of introducing DTCG namespacing machinery for what the schema already has a convention for.
Option C: SlotExample type; entries bundled in Component.instanceExamples (Rejected)
Introduce SlotExample extends SlotContent with an added slot: string; widen Component.instanceExamples to ComponentExample = InstanceExample | SlotExample discriminated by a kind field.
Rejected because:
slothard-codes a content entry to one slot; the same icon used instartVisualandendVisualrequires duplication.SlotExampleadds no structure overSlotContentonceslotis removed — it’s an alias.- The
kinddiscriminator is schema bookkeeping with no authoring value.InstanceExampleis consumed by docs/rendering tooling; slot content is consumed by slot-rendering and Figma default references. Bundling forces every consumer to filter bykind.
Option D: Slot default on SlotProp.default (Rejected)
Store the default-content reference on SlotProp.default.
Rejected because: SlotProp is the public prop API surface. Default content is per-variant element data — variant-sensitive and tied to a specific element in the tree (the slot-bound container), not to the prop definition.
Option E: Separate slot-content file (Rejected)
Store slot content in a separate file alongside the component definition.
Rejected because: Slot-content entries are component-specific. They belong in the component definition so that propConfigurations.<slotName> references and the Figma default reference can resolve via JSON Pointer paths inside the same spec file.
Option F: New top-level field on Element (Rejected)
Place the Figma default-fill reference as a new sibling field on Element (defaultContent, defaultChildren, or similar) alongside content and children.
Rejected because:
- Any name collides conceptually with existing
Elementfields (contentis text body;childrenis layout structure). - The reference is separated from the binding it documents:
childrenholds the slot binding; the new field holds the default. Two objects, one fact. - It pollutes
Element’s public surface with a field whose meaning depends on a sibling field’s shape and whose audience is a single platform (Figma).SlotBinding.$extensions['com.figma'].defaultcolocates with the binding and lives in the namespace that signals “design-tool provenance.”
Option G: Widen Element.content to accept slot-content keys (Rejected)
When Element.children is a slot binding, interpret a string in Element.content as a key into slotContentExamples instead of as literal text.
Rejected because: content is typed string | PropBinding. Making the same string mean “literal text” in one element shape and “slot-content key” in another is a conditional JSON Schema cannot express. A field’s meaning must not depend on a sibling field’s shape.
Decision
Type changes (types/)
| File | Change | Bump |
|---|---|---|
Component.ts | Add optional slotContentExamples?: Record<string, SlotContent> | MINOR |
Children.ts | Add SlotBinding with optional examples?: SlotContentRef[]; widen Children to string[] | SlotBinding | MINOR |
index.ts | Export SlotBinding | MINOR |
SlotContent and SlotContentRef are defined in ADR-046 (types/SlotContent.ts, types/SlotContentRef.ts) and imported here.
Children widening (types/Children.ts):
import type { PropBinding } from './PropBinding.js';import type { SlotContentRef } from './SlotContentRef.js';
/** * A slot binding: a `PropBinding` to a slot prop, optionally carrying authored * example fills for the slot. Each entry in `examples` is a `SlotContentRef` * (ADR-046) pointing into `Component.slotContentExamples` or into a * `Composition`. For now, emitters write at most one entry — Figma's authoring * default for the slot layer at index 0 — but the array shape leaves room for * additional examples without further schema change. `examples` is non- * contractual reference material; code consumers handle missing slots through * component logic and need not honor it. */export interface SlotBinding extends PropBinding { examples?: SlotContentRef[];}
export type Children = string[] | SlotBinding;Component extension (types/Component.ts):
# Before (after ADR-046)Component: title: string anatomy: Anatomy props?: Props subcomponents?: Subcomponents default: Variant variants?: Variants invalidVariantCombinations?: PropConfigurations[] metadata?: Metadata
# AfterComponent: ... # all fields above unchanged slotContentExamples?: Record<string, SlotContent> # newinstanceExamples is added in ADR-048.
Schema changes (schema/)
| File | Change | Bump |
|---|---|---|
component.schema.json | Add slotContentExamples to #/definitions/Component | MINOR |
component.schema.json | Add #/definitions/SlotBinding; update #/definitions/Children | MINOR |
slotContentExamples in #/definitions/Component/properties:
slotContentExamples: type: object description: "Named slot fill examples for this component. Each entry is a SlotContent (ADR-046). Referenced via SlotContentRef from SlotBinding.examples (Figma authoring defaults) and from Element.propConfigurations slot-prop entries (ADR-049)." patternProperties: "^[a-zA-Z0-9_-]+$": $ref: "#/definitions/SlotContent" additionalProperties: falseNew definitions in #/definitions:
SlotBinding: type: object description: "Slot binding: a PropBinding to a slot prop, optionally carrying authored example fills in `examples`. `examples[i]` is a SlotContentRef (ADR-046). Non-contractual reference material; code consumers ignore it." required: [$binding] properties: $binding: type: string description: "JSON Pointer to the bound slot prop." examples: type: array description: "Authored example fills for this slot. Emitters currently write at most one entry — Figma's authoring default at index 0." items: $ref: "#/definitions/SlotContentRef" additionalProperties: false
Children: oneOf: - type: array items: { type: string } - $ref: "#/definitions/SlotBinding"Out of scope for this ADR
Component.instanceExamplesandInstanceExample— see ADR-048PropConfigurationswidening to acceptSlotContentRef— see ADR-049- External
compositions.yamlfile schema — follow-on ADR
Notes
slotContentExamplesentries are slot-agnostic. The sameSlotContent(e.g., a glyph icon) can be referenced from multiplepropConfigurations.<slotName>sites and from multipleSlotBinding.examplesvalues. Authors are not forced to duplicate identical content per slot.SlotBinding.examplesis structurally available only in the slot-binding arm ofChildren. A container withchildren: string[](plain frame children) cannot carry slot examples — the schema enforces this through theChildrendiscriminant.- Per-variant Figma defaults are free.
childrenlives indefault.elementsandvariants[n].elements; different variants may declare different example fills for the same slot-bound container without any special-case mechanism. PropBindingis unchanged.SlotBinding extends PropBindingaddsexamplesonly for thechildren-binding case.PropBinding’s other use sites (content,instanceOf,visible) are unaffected.examplescarriesSlotContentRefobjects, not plain strings. This is the same discriminated reference shape used byElement.propConfigurationsslot-prop entries (ADR-049) — one resolver covers all slot-content reference sites.- Emitters currently write at most one entry.
examples[0]is Figma’s authoring default for the slot layer. The array shape is forward-compatible with additional authored examples without further schema change.
Type ↔ Schema Impact
- Symmetric: Yes
- Parity check:
Component.slotContentExamples?: Record<string, SlotContent>↔#/definitions/Component/properties/slotContentExamples(patternProperties→SlotContent)SlotBinding extends PropBinding { examples?: SlotContentRef[] }↔#/definitions/SlotBindingChildren = string[] | SlotBinding↔#/definitions/Children(oneOf)
Downstream Impact
| Consumer | Impact | Action required |
|---|---|---|
specs-from-figma | Must detect and emit slotContentExamples from slot layers; de-duplicate entries by structural equality across variants and slots — when two fills are identical, emit one entry and reference it from both call sites; must emit SlotBinding.examples (a single-element SlotContentRef array at index 0) on slot bindings in default.elements / variants[n].elements | Read new fields; implement slot-content detection with de-duplication; update output emitters |
specs-cli | Recompile; output includes slotContentExamples and SlotBinding.examples when present | Recompile; no breaking change |
specs-plugin-2 | Recompile; slot-content rendering is a follow-on capability | Recompile; pass through data initially |
Semver Decision
Version bump: 0.20.0 → 0.21.0 (MINOR)
Justification: Optional slotContentExamples field added to Component; new SlotBinding interface (extends PropBinding with optional examples?: SlotContentRef[]); Children widened from string[] | PropBinding to string[] | SlotBinding where SlotBinding is a structural superset of PropBinding (existing { $binding } values still validate). All additive → MINOR per Constitution §III.
Consequences
Component.slotContentExamplesandComponent.instanceExamples(ADR-048) are siblings: each holds one type with one purpose.instanceExamplesdocuments whole-component prop-configured usages;slotContentExamplesholds named slot fill examples.- Slot-content entries are slot-agnostic; binding to a specific slot happens at the reference site —
propConfigurations.<slotName>viaSlotContentRef(ADR-049) for authored usage,SlotBinding.examples[0]for Figma authoring provenance. SlotBinding.examplescolocates authored example fills with the slot binding insideElement.children. BothSlotBinding.examples[i]andpropConfigurations.<slotName>use the sameSlotContentRefshape — one resolver covers all slot-content reference sites. Per-variant Figma defaults fall out becausechildrenalready lives onElement.- The
examplesconvention now spansProps(StringProp.examples,NumberProp.examples) andSlotBinding(examples), under one consistent name for authored sample content.