Computed Design TokensPublished See discussion on Twitter
Alternative titles: Dependent design tokens, computed theme values
For the past few weeks I've been working on updating the theme within my
specifically around the colors that I'm using within the theme and the ones that
map to "functional roles" (e.g.
secondary, ... etc). Through this
work I've been exploring a few different patterns that I wanted to share more
Before diving into the two things I've been tinkering with I want to step back
and define the problem space a bit, specifically with this work I've been
thinking about how to enable consumers of the above components package to theme
the system without needing to re-construct the whole
theme object that the
In a traditional theme, this customization might be fairly basic by using object spread to override some values:
This works really well for flat theme shapes (which might be worthy of another blog post in itself), but for nested themes, this soon gets out of hand:
Another complication that arises when customizing a theme is dependent theme
values, let's say that the theme shape also contains some styles for different
variants of a component (e.g. styled-system's
variant), and those styles use the same values from other parts of theme:
If the system defines the theme like above, and a consumer wants to change the
primary color to
cornflowerblue, the consumer may not know that they need to
buttons.primary.color value as well.
These two issues:
- Less than ideal ergonomics for overriding themes
- Dependent theme values don't react to overrides
make theme overriding a particular indersting challenge.
So let's finally dive into some of the ideas I've been working on, specifically two concepts:
The first idea that I started workshopping was to use a
to define values within theme that can reference other values within the theme.
I didn't mention above, but a common workaround I've seen for the second problem noted is defining the theme primitives outside of the scope of the theme object, and referencing those later:
However this only solves the referencing issue for the file in which the theme is defined (frequently that is within the source package for the system). With getters however, you move that derivation time to runtime rather than module load time:
Now, the button primary color inherits the primary color specified on the theme, allowing us to override only that value and have the button styles inherit those changes:
Essentially using the same syntax that you might use on a traditional
within the theme shape itself:
With this method, we've moved the derivation from the reference of the value
with the getter method to the callsite of this
themer function (which has a
few trade-offs depending on how dynamic your theme needs to be).
To experiment with this more deeply, I built a small package that you can try