A simple React Theme Component using CSS variables
At Trabe we had to create a component library with multiple look and feels for some client. This is the solution we came up with.
Our needs
- We like to use CSS Modules so the solution should be compatible.
- We should be able to show Components with different themes at the same time.
- It should require minimal tooling.
The solution
Using CSS variables to customize our CSS files, and writing our components as we would normally do.
For example, a small Button
component could look like this:
But instead of having a button.css
like this:
We would have:
Note that we use BEM conventions, adapting them to our liking, but the styles themselves are self explanatory.
Where do we get those vars from? We inject them on a wrapper element that surrounds the components we want to style.
How do we inject them? With a Theme
Component that gets the variables as props, creates a div
that acts as a wrapper and passes them on its style
prop.
We could then have some custom themes to store those variables and export them within the Theme
component itself, like you see here:
Our two themes could look like this Jedi theme:
Or this Sith theme:
Now, if we want to show a Button
using either theme we just have to wrap it on a Theme
with the according variables:
This should look more or less like this (if you don’t have the Sith Font and the Jedi Font installed).
If you want a Jedi app you just have to wrap all your app on a Jedi theme. You can also do it on smaller portions of it.
Sometimes you may even want to see your component in every theme you have. We do so in our Docz documentation with a little helper that renders a component in every theme available.
The gotchas
If you like this approach and you want to use it you have to plan your themes accordingly.
For example: if you want, say, rounded borders on one of theButton
themes you will have to set a border-radius
property on your styles, and remember to set it to none
on all the themes that have squared borders.
You will also have to wrap all the components that support theming. Things like modals and everything outside a Theme
wrapper would look unstyled.
If your var files grow a lot, FOUC could become a problem at some point but we had none as of time of writing.
Conclusion
Maybe all this is overkill, maybe it has no sense at all, but existing solutions did not fit our needs and we like this simple, clean approach.
You may feel uncomfortable using CSS variables for theming, but they are widely supported and they let you change most visual aspects of your components.
We’d love to hear your thoughts and solutions to this problem.