Nested component instance: how do I keep nested property values across "parent variants"?

Given a button component with some variants for color:

… and given a popover-button component with the button component nested into it:

I have some instances of the popover-button, for which I would like to override the variant-property for color (in the main component, the variant-property is set to black).

For the current variant of the popover-button, this setup is fine.

However, when the variant property of the popover-button is switched, it reverts to the initial variant property value of the nested button-component, the way it is defined in the main component. My override for variant property value for this nested button-component is ignored.

My question would be: how can I make sure that a component’s instance respects the overridden nested component instance’ variant property override across parent component instance variants?

An excerpt (copy) of my design system can be found here, so it is clear how I have set things up:

Hi @Jeroen2, Thank you for reaching out and sharing your design file with us. I appreciate it!

I just want to make sure I’m understanding correctly - do you override the variant property value for the nested button-component at the main component level?

If so, based on some internal exploration and testing on my end, unfortunately, it appears that if you perform a swap at the component level, instances of those components do not preserve overrides.

Figma only preserves overrides on instances when swaps are performed within that instance.

I hope this information helps clarify things a bit. If there’s anything I’ve misunderstood, please let us know.

Thanks,

Thank you Junko3 for taking some time to understand the situation. In the document I referenced above, I created an example-frame which contains instances of the popover-button component. What follows is quite a detailed analysis. Please bear with me.

Each of the popover-button components displayed here is an instance of the main component.

  • “original” has no variant overrides.
  • “white” has the button color variant overriden to “white”.
  • “danger” has the button color variant overridden to “danger”
  • “success” has the button color variant overridden to “success”

:warning: Important note #1
It is important to note that the nested button-component has another component nested into it, called “interactivity background”. This interactivity background has color variants that match the button color variants.
To create interactive states (hover/active) The interactivity background has additional variants for this: “state” with values “idle”, “hover” and “active”. image
image

:warning: important note #2
Additionally, it is good to know that the variant-property “opened” is used to control the open/close state, including the button interactive state (idle to active)
image

I notice that the unexpected behavior is bifold: I see behaviors differ in design mode and in prototype mode

1. In design mode
In design mode, switching the popover-component to another variant preserves the override on the direct “child component”, but not on any successively nested “child-in-child components”. In my example it seems like for children-in-children, their original variant overrides are discarded, even if in the main component where they are nested they had an override. Allow me to elaborate.

Situation Visual result
step 0: without any variants toggled
step 1: toggle opened/closed first time
step 2: toggle opened/closed second time, back to initial open/close state.

Because of this, I wanted to try out what was going on with the “child-in-child” interactivity background component. Hence, the default interactivity background for the button color variant in the main component of “popover button” is now red:

This is the same situation as in the first image, but then with the “original” button with a read active state. For clarity, I used some other color instead of “danger”. Let’s repeat the scenario above!

Situation Visual result
step 0: no variant switch
step 1: 1st time switch of variant: back to the default variant
step 2: 2nd time switch of variant: revert to the variant before the first switch.

As you can see, the color of “interactivity background” is not reverted to the color variant that was initially set for the overridden color variant of its parent component “button”, but rather to the color variant of the “un-overriden” button color variant for the parent-of-the-parent “popover button”.

It’s as if only shallow nested instances of components have their overrides preserved, not deep nested instances of components. But here we are entering the territory of recursion, which is inherently hard to wrap your head around. For me, it definitely is.

Brace yourself! As promised, there is another unexpected behavior… :sweat_smile:

2. prototype mode
The popover-button component is set up to open/close upon click of the nested button. To ensure that all variant properties are correctly set for child components (and for children of children), the variant change upon click is set to reset component states. At least, that was my theory. On the flip side, not resetting component states at all results in variants of nested component instances not changing at all, which is also undesirable behavior.

This is what happens if I follow the same steps as above:

Situation Visual result
step 0: without any variants toggled
step 1: toggle opened/closed first time
step 2: toggle opened/closed second time, back to initial open/close state.

It appears that during step 1 all overrides are reset. But during step 2, all overrides are reverted back.

So here’s the bifold:

Design mode Prototype mode
It’s as if only shallow nested instances of components have their overrides preserved, not deep nested instances of components. It appears that during step 1 all overrides are reset. But during step 2, all overrides are reverted back.

As a result, I am not quite sure what to do.

  • Is this something I can fix in the setup of my design system?
  • Or is it something worth submitting a feature request for?
  • Or… is it a bug that I should report?