Skip to content

Bound content on a collapsed-primitive root bakes to a literal for Heading but not Text #284

Description

@nathanacurtis

Found while verifying the ADR 058 wrapper re-expansion in the render path, against workspaces/eg in specs-testing.

Symptom

A collapsed-primitive component whose root content is bound to a prop comes back with a literal string instead of the binding:

# baseline — specs/Heading/variants.yaml
default:
  elements:
    root:
      content: { $binding: "#/props/text" }

# round-trip
default:
  elements:
    root:
      content: "Heading"

Why this is not simply "bindings on collapsed roots are unsupported"

Text — same shape, same prop name — round-trips the binding correctly:

Component root anatomy baseline content round-trip content
Text type: text, originalName: Text {$binding: "#/props/text"} {$binding: "#/props/text"}
Heading type: text, originalName: Text {$binding: "#/props/text"} "Heading"

So the mechanism works. Something specific to Heading prevents the TEXT component property reference from being attached to (or read back from) the recreated leaf.

What has been ruled out

  • Not the collapse re-expansion itself — both components rebuild the wrapper and recreate the leaf, and both round-trip an identical anatomy.root including $extensions.com.figma.originalName.
  • Not a per-variant content override — neither component overrides root.content in any variant; both bind only in default. Heading has 8 variants, Text has 54, so variant count is not the discriminator.
  • Not a missing propertyprops.text is present as a string prop on both sides for both components.
  • No warning is emitted. specs render specs/Heading completes with no output at all, so the writer believes it attached the binding. Compare Icon, where the same class of failure does warn (binding "root.characters" → "name": no component property — skipping).

Candidate lead, unverified

Heading carries a code-only prop that Text does not:

props.role.$extensions:
  com.figma:
    type: VARIANT
    source:
      kind: codeOnlyProp
      layer: Heading role
      instanceOf: "Code only props /_ / Heading role"

That prop round-trips as an ordinary VARIANT prop (its $extensions are dropped, nullable: false is added) — expected, since code-only props are not implemented (#269). Whether the code-only prop's presence interferes with property creation or binding order for text is unverified and should be the first thing checked.

Reproduce

cd workspaces/eg
specs render specs/Heading --overwrite --page <id>
specs generate --from-bridge --node <returned nodeId>

Then compare default.elements.root.content against the baseline. Repeat with specs/Text for the passing case.

Verification

  • Heading's default.elements.root.content round-trips as {$binding: "#/props/text"}
  • Text continues to round-trip its binding unchanged
  • If the writer genuinely cannot attach the reference in this case, it must warn rather than silently baking a literal

Part of #281.

Metadata

Metadata

Assignees

No one assigned

    Labels

    generatorspecs-from-figma processing engine

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions