Initial support for variables and design tokens
Use the Figma API to parse variables. There are three main areas we now parse:
- boundVariables from nodes that use variables. This change supports background color, stroke color, text color, and rounded corner sizes.
- explicitVariableModes from nodes where Figma set a mode to be used on that node and children.
- All the variable data (collections, modes, variables) from a new variables API call.
boundVariables and explicitVariableModes are stored in the view generated for each node. The rest of the variable data is stored in a variable map data structure stored in the serialized file.
Three new functions are introduced to override the current theme or mode.
- DesignVariableCollection(themeName) declares a CompositionLocalProvider object that changes the current theme (collection in Figma) to the specified theme name. This theme name must exist as a collection in the Figma file, or it will do nothing.
- DesignVariableModeValues(modeValues) declares a CompositionLocalProvider hash map that maps collection names to mode names. This changes the mode for the specified collection. For example if the default mode in Figma is set to 'light', this can be used to change the mode to 'dark'.
- DesignMaterialThemeProvider(Boolean) declares a CompositionLocalProvider boolean to specify whether the material theme on the device should be used to override the theme in Figma. This only works for variables that were created from the Material Theme Builder plugin, or were made in a way to match -- e.g. the collection name is "material-theme" and the variable names match the Material Theme names such as "Schemes/Primary".
When it is time to render a node that uses variables, we need to translate the variable into a value. This is achieved by using the helper class VariableManager, which takes the current VariableState containing the current collection and mode, along with the variable map in the serialized file, and looks up the correct value for the variable.
I'm leaning into the CompositionLocal method. It lines up with how Material already works which is a big plus. I think the downside of it is that it makes it harder to change the collection or mode partway down the tree, but that seems like that can just be handled by doing a ComponentReplacement with a node that handles that part. It'd be ugly, since you need to change your Figma design to use/replace the placeholder, but it also (probably?) won't be that common.
That said, I don't know enough about the actual use cases we want to support. The design doc implies some in it's requirements, but it would be helpful to see a few laid out specifically. Something like "Designer has two collections, each with two modes that share mode names and share variable names. Designer wants to be able to switch between collections at runtime". I don't know if the one I described really makes sense or not. I really need to take some more time to experiment with Figma to be more helpful in reviews like this.
Snapshot diff report vs base branch: main Last updated: Wed May 29 16:02:57 PDT 2024, Sha: 57505bd972634a6a15861b910f8a005bff097c2b
@timothyfroehlich any idea why roborazzi test is showing this diff?
@timothyfroehlich any idea why roborazzi test is showing this diff?
It seems only the tests from the same design file have the diffs?
@timothyfroehlich any idea why roborazzi test is showing this diff?
It seems only the tests from the same design file have the diffs?
I'm really not sure. I tried to recreate this with John's PR but his isn't currently building.
I'll try to look into it more, but I'd say that if it's the only thing holding up this PR then it's not worth waiting for. If it happens again then I'll dig into it more and hopefully it'll happen in a smaller PR where it'll be easier to isolate the changes.
I've finished my review. It mostly looks good, but I think deduplicating the code will make implementation of String and Boolean variables much easier, and will help prevent bugs that stem from changes to the code for only one of the variable types.
Also, a thought, it might be helpful to use the name "DesignVariable" (or similar) instead of "Variable", to help with discussion of the code and with documentation in general.






















