Nested interactive instances trumping overrides at main component level

Hi folks! Perhaps this is a bug, or perhaps I am misunderstanding the limitations and proper use of interactive components. I am hoping it’s the former.

We are using a multi-library file structure for our Figma component library. We have a “core” library file where our foundational components live, and instances of those components are pulled into “product specific” libraries further downstream where overrides are made and we introduce branding to our components. This allows all our products maintain their unique looks while still referencing a single source of truth.

When the interactive components beta was released I expected to be able to set up interactions at that core level, but those interactions fail at the product-specific library level. The original component from the core ends up trumping the overrides I made in the product-specific library.

Here is a quick demo of the issue:

If you’d like to replicate this process, these are the steps

  1. Create two library files. One called “core” and one called “Product A”
  2. In the “core” library file, create two rectangles with different solid fill colors
  3. Turn both rectangles into components, and then combine both components into a variant.
  4. Set up a “while hovering, change to rectangle 2" interaction on the first rectangle, and a “Mouse leave, change to rectangle 1” interaction on the second. This should mimic a basic button hover interaction.
  5. Name this component “Core rectangle” and publish the library.
  6. In the “Product A” file, insert two instances of “Core rectangle.” Set one of them to the default variant, and the other to the hover state.
  7. Make both of these instances main components, and then combine both of those components into a variants.
  8. Override the fill color of both rectangles in the “Product A” file.
  9. Name this component “Product A Rectangle”
  10. Place an instance of “Product A Rectangle” on a frame and click the “play” button to test the interaction

What should happen now is when you hover on “Product A Rectangle” the hover state will jump to the original color of the “Core rectangle” component. And when your mouse leaves, it will also be the original color from the “Core rectangle” component. The color overrides made in the “Product A” components are not visible.

My expectation would be that overrides set in “Product A” would persist.

My question is: is this a bug of interactive components? Or is the best practice to set up prototype connections at the surface level (as opposed to nested instances) of components?

6 Likes

This is good to know since my team (40 designers) are currently considering the same model of a core library inherited by multiple theme libraries. Outside of your posted issue, how has this model worked for you.

Hey Eric,

I’ve since moved companies and only recently employed a similar core → product-specific library here on my new team, and would be interested to try and replicate my issue now that a year has passed and the feature is out of beta. Thanks for your reply!

To answer your question, so far so good! Right now we’re handling interactive states in the product specific libraries, making the brandless/core library solely responsible for layer structure.

On the team I’m on today, we have 2 of 3 products opting into using the core library to underpin foundational components that the core offers. One of those two teams only just started using it, but it’s helping them stand up a strong set of components faster than they otherwise would’ve been able to. Wish I had more to report back on. Need some more time to get a good sense of what all the negative tradeoffs will be (there’s always something) and exactly how costly they really are. We’ll see! Like I said, so far it’s been positive.

Thanks for the insight. One thing I was thinking as a possible drawback is in the case that a product library creates a component for themselves but it’s also a good adoption for the core. Normally the style trickles down from core but in this case, it was built downstream. How do you then get it into the core library so it’s used as a base for all products, without breaking it at the product level where it was built? It usually takes a little time to get things in at the top level so it’ll likely be in use to some varying degree. Not the end of world but something I might consider trying to mitigate.

Yeah, great question. We’ve encountered something similar, I think: when our team of designers started at this organization, we were given a product-specific component left behind by a prior team. We’re retroactively introducing this idea of a core library serving as the foundation to branded/product-specific components, so for all the components that existed when we arrived we’ve had to retrofit them to contain core instances. In many cases it was easier to start entirely from scratch than insert a core instance into an existing component. We knew we would lose all overrides anyway because the layer structure was changing so dramatically.

In terms of how this affected in-flight work, product designers opted to work with a mix of the legacy components and new retrofitted ones supported by the core as they were being published. We prefixed legacy components with an :x: emoji to signal it would eventually be deprecated and a new version was available for use. Between Q4 '21 and Q1 '22 when the majority of folks were on holiday we did a big round of clean up and clear out the publish-panel. This meant new files started in 2022 would only pull in the new core-supported components.

Related to the override loss tradeoff, @Greg_Huntoon recently tweeted an impressive video demoing how he uses the Automator plugin to preserve overrides when altering an instance’s underlying layer structure.

Great resource. I think in a perfect world we would build a library robust enough that the stream only needs to flow one way and you wouldn’t need to add from product libraries, but situations happen.

Totally agree. I suppose it depends a lot on whether those product-specific libraries have any kind of relationship when it comes to larger components. They’ll likely all have the foundational things like form elements and buttons which make sense to share, but it could come up that they all also want to share something like a log-in/sign-up modal. All depends on how much flexibility each product wants to have.

I think a main benefit a core library can offer product-specific ones is setting standards for what a well-built button component looks like, or well-built text input. I remember struggling to figure out how to put together a floating label input field that accounted for a state where the label was inside the input (where the value would ultimately go) as well a state where it sat in an empty space on the top border of the input. I also was determined to make sure text overrides weren’t lost when switching between those states. I’d definitely put that one in a core library, even if 1 or (or none!) product specific libraries needed it today, just to make sure they have the option to grab a well constructed floating label input if ever they needed one.