Skip to main content
Question

Extended collections: collection variant and mode share one value

  • August 19, 2026
  • 2 replies
  • 15 views

Torsten_Daeges

Severity: Blocking — we cannot adopt extended collections

The problem

A node holds one mode entry per collection family, and an extension's mode IDs are encoded with the collection ID. So choosing a brand and choosing a mode are one choice, not two. A node cannot say "use Brand B, keep the mode you inherit" — or "use Hover, keep the brand you inherit".

This is not a broken lookup: ExtendedVariableCollection.modes[] exposes parentModeId, so the parent↔extension mode mapping already exists. The problem is that there is only one slot to write it to.

The ask: decouple the data source (collection variant) from the context state (mode), each independently settable and inheritable.

Our setup

One mode axis per collection, because modes are per-collection and mode counts are capped:

Collection Axis Mode set by
Semantic Light / Dark designer, on the artboard
Viewport s / m / l / xl component (responsive variants)
Interaction state Idle / Hover / Pressed component (state variants)

Brand is a fourth, orthogonal axis. Component-set modes are intentional — a variant sets the mode so nested components switch with it.

Repro A — switching brand discards the inherited mode

  1. Collection Semantic (Light, Dark), extended as Brand B.
  2. Frame set to Brand A → Dark; on a child frame, switch the variant to Brand B.

Observed: child resolves to Brand B → Light. Expected: variant changes, mode keeps inheriting. (Also: https://forum.figma.com/share-your-feedback-26/extended-collections-doesn-t-respect-mode-hierarchy-48123)

Repro B — the blocking one

  1. Collections Interaction state (Idle, Hover) and Semantic (Light, Dark), both extended as Brand B.
  2. A component's Hover variant sets an explicit mode Interaction state → Hover.
  3. Set a top-level frame to Brand B, insert an instance, switch it to Hover.

Observed: the instance resolves state variables against the parent collection — the pin authored in the component carries the parent collection with it and silently overrides the brand, in every instance and every file. Expected: the pin selects the mode; the variant keeps inheriting.

The rule this produces: a collection may either be extended, or have its modes pinned by components — never both. Every state and viewport collection we have falls on the wrong side.

No plugin workaround

parentModeId makes rewriting artboard modes trivial, but not Repro B: rewriting a component's pin hard-codes a brand into the component; clearing it removes the state behaviour the component exists for. setExplicitVariableModeForCollection has no way to express "pin the mode, inherit the variant".

What a narrow fix would miss

Preserving the mode when a user switches variant fixes Repro A and leaves us exactly as blocked. The reset is the symptom; the coupled slot is the cause. Any fix keeping one slot per family still cannot express a component that pins state while inheriting brand — and that is what modes were built for.

Why it matters beyond our file

Splitting axes across collections is what Figma's own constraints push teams toward. Extended collections are only applicable to collections no component ever pins — in practice, a single colour layer. The more carefully a system was factored before the release, the less of it survives.

Same on the code side: token pipelines model brand and theme as independent dimensions (in DTCG, a brand resolver overriding sets, with theme/viewport/state as modifiers). Value-level sync works well; what breaks is the node-level assignment, which has to be flattened into one coupled slot on import — irreversibly. Decoupling the two dimensions would make extended collections the structural mirror of how tokens are already authored in code.

2 replies

Jaycee Lewis
Figmate

What’s up ​@Torsten_Daeges 🤩 Thanks for the detailed post. I love these questions. Let’s dig in.

One piece I can confirm from the public API docs: setExplicitVariableModeForCollection() takes a collection and a mode ID, and both explicitVariableModes and resolvedVariableModes are keyed as one mode value per collection. There is no parameter that expresses "pin this mode, keep inheriting the variant." So your conclusion that the current API has no way to author that is correct ✅

What I can't confirm from docs is the resolution behavior in either repro — what happens to an inherited mode when the variant switches, and how a component-authored pin resolves for an instance sitting under an extension. Super interesting for sure.

Could you share a file link with both repros set up? You can invite support-share@figma.com as a viewer — this won't affect your billing or take up a seat on your plan. A full-screen, unzoomed screen recording of Repro B would help a lot, since the "pin carries the parent collection with it" part is the piece I most want them to see happen live rather than read described. 

Talk soon! — Jaycee


Torsten_Daeges
  • Author
  • New Participant
  • August 22, 2026

Hello Jaycee! Thanks for having a look at it — and for confirming the API side; that matches what we found.

I've prepared a detailed repro file and invited support-share@figma.com as a viewer. Since everything is local to the file, I could also send it as a .fig (couldn’t upload here). Here’s the link:
 


The full-screen recording of Repro B is here: 
https://torsten.daeges.de/files/repro_b_extended_collection.mp4

Building the file produced some further findings, which I've detailed below:
 

Thanks for confirming the API side — that matches what we found, and it is why we stopped looking for a plugin workaround.

Everything below is now measured rather than inferred, and all of it is demonstrable from the UI. Both repros are local to a single file: no library, no primitives layer, no aliases, no nested components. Nothing in the original report needs correcting, but the picture is sharper. One of the two reported cases turns out to be a single inconsistent path rather than a consequence of the model; the other is confirmed and worse than we described; and there is a third case we had not seen at all.

The menu already presents two dimensions

The layer mode menu lists Collection and Mode as two separate groups, and the Auto entry displays the resolved pair, e.g. Auto (Theme (Brand A) · Dark). An entry in the Collection group names no mode — yet selecting it overwrites the inherited mode and drops the layer out of Auto.

What happens in each of the four paths

We tested all four combinations of starting state × which group is clicked:

Starting state Click in… Other dimension Layer still inherits?
Explicit assignment Mode ✔ collection kept — (was already explicit)
Explicit assignment Collection ✔ mode kept — (was already explicit)
Auto Mode ✔ resolved collection carried over ✘ no — value frozen ¹
Auto Collection ✘ falls back to the default mode ✘ no

¹ The value is correct, but it was copied into an explicit assignment: the layer leaves Auto, so later changes on the ancestor no longer reach it. See Repro C.

Verified with an extension as the inherited collection (parent on Theme (Brand B) · Dark), so row 3 is not an artefact of the parent collection being the default.

Two separate findings sit in these two columns.

Third column — one deviating cell. The three other rows show that the resolved value is available at exactly the point where row 4 ignores it. Repro A therefore looks like a single inconsistent path rather than a limitation of the model — it should be cheap to fix, and we would welcome that fix on its own.

Fourth column — the rule behind all of this. Any explicit selection made from Auto, in either group, ends inheritance in both dimensions. That is what produces Repros B and C, and it does not go away when row 4 is corrected.

Repro B — confirmed, and there is no brand-neutral pin

This is the one you asked to see live; the recording is linked above, and page Repro B shows it as four frames.

Part 1. A component with Idle and Hover variants. Only the Hover variant carries a pin (State → Hover), authored while Brand A was resolved. Placed in a frame set to Brand B for both collections:

  • the Idle instance, which has no pin, resolves correctly to Brand B / Idle;
  • the Hover instance resolves Theme to Brand B but State to Brand A.

The only difference between the two rows is the existence of the pin. Theme still follows, so the pin drags back only its own family.

Part 2. The obvious response is to author the pin in the other brand's context, so we did — a second copy of the component, pin set while Brand B was resolved. It is now correct in Brand B and wrong in Brand A, exactly mirrored.

In both parts the Hover border renders identically in both brands, although the two frames specify different brands. A pin can only ever be authored in one brand's context, and that brand travels with it. There is no authoring choice that works for both.

The consequence for us: with the current behaviour, the only way to ship a component library supporting more than one brand is one component set per brand — not just a second set of variables. At that point extended collections provide nothing that swapping libraries does not already provide, while additionally requiring Enterprise, a migration that clears every existing mode assignment, and a permanent binding to a parent collection. For component libraries, the feature is currently outperformed by the workaround it was meant to replace.

Repro C — selecting a mode silently freezes the collection (new)

  1. Parent frame → Theme (Brand B) · Dark. Child on Auto, correctly resolving to Brand B / Dark.
  2. On the child, choose Light from the Mode group. Child resolves to Brand B / Light — looks correct.
  3. Change the parent to Theme (Brand A).

Observed: the child stays on Brand B / Light. The mode selection had written the collection as well, copying whatever was resolved at that moment. Expected: the child follows the parent to Brand A / Light.

This is the more damaging case in daily use, because nothing is visibly wrong when it happens. The breakage surfaces much later, when a brand change on an ancestor silently fails to reach part of the tree.

What this rules out

Inheritance itself is not broken. A child left on Auto follows its parent on both dimensions, including changes made afterwards. The coupling appears only at the moment something is set explicitly: a layer holds one value per collection family, and there is no partial state.

Minor, related

The Appearance panel labels the family with the root collection's name (Theme (Brand A)) even when the layer resolves to an extension. In a multi-brand file the panel does not indicate which brand is actually in effect.

What this changes about our request

Nothing in substance. The menu already communicates two dimensions; we are asking that the stored state match it, so each can be set independently and left on Auto independently.

We would separate the two asks, though, since they differ in size:

  1. Small: make row 4 behave like rows 1–3 — carry the resolved mode over when a collection variant is selected from Auto.
  2. The actual request: allow a partial assignment, so one dimension can be pinned while the other keeps inheriting.

Only the second unblocks us. Repro B remains the blocking case: as long as a mode pin inside a component carries a collection with it, no collection whose modes are set by components can be extended at all — and in our system that is every state and viewport collection we have.

Guide to the file

  • Page Setup — the two collections, their modes, their extensions and the variables involved. Worth reading first; the rest assumes it.
  • Page Repro A — collection selection discards the inherited mode. Includes a baseline frame on Auto where inheritance works, and a manually built expected state.
  • Page Repro B — Parts 1 and 2 as described above, plus both component definitions with the brand each pin was authored in.
  • Page Matrix — the four paths above.
  • Page Repro C — found while building this file, not part of the original report. Three steps, each as its own frame, with the menu before and after the click.

Layer names carry their mode assignment in brackets, e.g. Child [Theme: Brand B]. Colours encode both axes: hue = brand, lightness = mode. Text labels are bound to string variables, so each frame states which collection and mode it actually resolved to.

One note on the Expected frames: each can be produced as an appearance by pinning both dimensions by hand. None of them can exist as an inheriting state — which is what this report is about.