When I wrote
Styled System on React Hooks
I explored what it might look like in the near future to
consume a theme context provided by the conventional
<ThemeProvider/> through the pane of styled-system and
React Hooks. In reality, we don't need
part of our CSS-in-JS package either. Which means we can
split it out, chop it up, and
So let's start at the beginning. For those who aren't already aware the typical theme process uses something like this:
styled components can access the theme through
and css prop can access through a function
This has a couple of different issues such as needing to
wrap components in a
ThemeProvider when testing,
over-reliance on direct use of token values instead of
building component-level APIs, etc
So if we're not locked in to using a single
package, what if we still want to use that approach, either
because we like it and don't need much different or because
we want to migrate to a more modular approach where themes
come from multiple contexts. Well, that uses React's context
support directly and hooks.
We take a set of default values and create a new theme
context. Then we export a provider and a hook that uses the
context. This allows us to statically type the values our
theme accepts (something that could be more troublesome in
ThemeProvider) so when the defaults are replaced
by a user, we keep our types.
One interesting facet of themes is that the theme object typically becomes a merging of component-level token API translations and generic design tokens. Sometimes this even goes so far as to create "JSON Specs" for the colors and sizing.
OutlineButton, for example, might expose knobs to
change the color of the border and text at the same time. We
could call this
color and it could be associated with a
OutlineButton can now expose this via its own context
and we can translate our theme tokens into buttons.
We've now allowed the
OutlineButton to expose an API for
changing values that we can map our generic tokens onto in
an explicit way. If we need another
OutlineButton, we can
construct another using our design tokens and now we also
have a list of all the
OutlineButtons in wide use.
We could also grab
our-theme from context as well to do
the translation in a slightly different place and allow any
overrides that have been made to the core tokens to be used
(such as a new products brand color).
By using native context APIs (through hooks) we can avoid property conflicts between different NPM packages. We can ship themes using the names that make the most sense, not prefixed with the component name because the context is our namespace.
The point here isn't that we should immediately go off and split all of our themes up into a million little contexts. It is more that we've opened up some additional design space relating to how we build and distribute components. We can explore how to compose multiple disparate components into a usable, distributable collection that leverages our brand and our types.