Theming React Components with CSS Variables

Two ways to theme React components using CSS custom properties.

Eden Ella
Bits and Pieces

--

TL:DR — Watch this :)

Up until lately, whenever we’d think of changing themes in run-time, we’d immediately go for some sort of a JS/JSX solution. That could be either theming through higher-order components, passing themes as props (usually with the help of React.Context) or using popular libraries like styled-components.

Today, thanks to ‘CSS custom properties’, a.k.a ‘CSS variables’, we shift the weight towards plain CSS. That is to say, there’s still a need for some JavaScript but as you’ll see, it plays a much minor role.

Why Use CSS Custom Properties for Themes

Performance Optimization

As a rule of thumb, the more we use CSS and the less we use JS, the better our apps’ performance will be (with a few exceptions). The reasons for that go well beyond the scope of this post.

Universal Code is Easier to Maintain and Scale

Another reason would be to make your UI components as universal as possible. That means using universal technologies (JS, CSS, HTML), as oppose to library/framework-specific code (for example, ‘styled-components’ or even embedding you styling in your React components).

That’s important for maintenance and scale. It’s much easier to “translate” UI components from one technology stack to another when most of the styling is done using plain CSS. So, whether you’re migrating your codebase to another technology stack or even creating another app using different technologies, you’d find it much easier to do so when most of the styling is done using plain CSS.

As an important side note, maintenance and scalability is also a function of using the right tools. I personally use Bit to share and organize all my UI components. That’s my way of, essentially, building a design system for my team without losing precious time over that. Unlike other tools and methodologies, Bit gives you the freedom to continuously push new UI components, from any codebase to your own component collection in Bit.dev. The alternative would be to work on a whole new project (a UI library project) just to document and share UI components.

Learn about reusing your styling rules using Bit:

Browsing through shared React component in Bit.dev

Set CSS Variables

CSS variables (and the full support that they have in all popular browsers) are something we were all looking forwards to. They’re not only a solution to storing color palettes and standardized sizes (something we’re used to solving by pre-processors like SASS) but they actually transform CSS from being completely static to having some sort of dynamics (something that pre-processors are not able to deliver, at least not in run-time).

The syntax for declaring a CSS variable is quite simple: —- is added to the variable name. Note that variable names are case-sensitive.

.example-class {
--example-custom-prop: #00000;
}

To use a variable, wrap the variable name with var() .

.example-class {
color: var(--example-custom-prop);
}

Methodology #1: Scope Custom CSS Props

Definitions:

  • Theme: A CSS class with a set of custom CSS properties. The class serves as a way to scope sets of custom CSS properties. Sets of CSS custom properties are named the same across themes.

Workflow:

  1. All themeable CSS properties, for all components, will be defined as custom CSS properties (as opposed to constant values).
  2. The App component will be the only component that has a theme className (e.g, .dark-theme ).
  3. Changing themes will be done by replacing the className of the App component. All descendants of the App component will receive new custom CSS properties by inheritance.

Example

Set the custom CSS properties in their corresponding classes.

.light-theme {
--primary-color: #007bff;
--disabled-color: #6c757d;
}.dark-theme {
--primary-color: #000000;
--disabled-color: #4a4a4b;
}

Add the default theme class to the App component.

const App = () =>  { return (    <div id='app' className="light-theme">
...
</div>
);
}

Create a JS function to replace the App className.

const replaceTheme = (newThemeName) => {  document.getElementById('app').className = newThemeName;}

A Demo Project

https://theme-react-first.firebaseapp.com/

Methodology #2: Set CSS Custom Props

Definitions:

  • Selected properties: CSS variables that are used directly as CSS properties to all themeable UI components.
  • Stored properties: CSS variables that are not used directly to style components but are used to store sets of values.
  • Theme: A set of stored properties, representing a particular styling.

Workflow:

  1. We set the ‘selected properties’ to a theme (a set of ‘stored properties’). This will serve as our default theme.
  2. All themeable CSS properties, for all components, will be defined as selected properties (as opposed to constant values or sets of CSS variables, representing a specific and constant theme).
  3. We use JS to replace all variables that serve as values for our ‘selected properties’ with another set of variables from the ‘stored properties’.

Example

In the example below I’ve also set my selected values to the default theme ‘light’ by using the variables to my light theme, as values.

Below, an example CSS for a Button component. Notice how I’ve set the relevant properties to --selected- and not directly to a themed value. So, for example, instead of using--dark-primary-color I’m using --selected-primary-color .

Build a ‘setTheme’ Function

The function below collects all the --selectedCSS variables and sets their values to the new theme variables. Here, JS is used only to replace the values of one limited set of CSS variables, to another set of values.
(For this to work, make sure you name your CSS properties in a consistent format).

The key method here is the setProperty method that lets us select a CSS variable/custom property and set a new value.

document.documentElement.style.setProperty(<CSS Variable>, <Value>)

That’s about it :)

A Demo Project

https://theme-react.firebaseapp.com/

Build Apps with reusable components, just like Lego

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

Learn more:

--

--