</>Jonathan Harrell

Main Menu

Site Tools

Live Theming with CSS Variables

CSS variables are opening up exciting new possibilities, like creating a very performant live theme editor that dynamically updates CSS values.

CSS variables, now enjoying fairly good browser support, are opening up exciting possibilities for more dynamic styling. Having true CSS variables means that we can get and set their values in JavaScript, allowing us to build cool features like live theming.

The process is fairly simple — setup initial variable values in CSS, markup some inputs, add event listeners and set the values in JavaScript. Let’s get started!

Set CSS variables using JavaScript to create a live theming experience. Predefined Themes Colors BACKGROUND COLOR TEXT COLOR LABEL COLOR Typography BASE FONT SIZE BASE LINE HEIGHT Lorem ipsum dolor sit amet, consectetur adipisicing elit a blanditiis. Lorem ipsum dolor sit amet, consectetur adipisicing elit. A blanditiis commodi consequuntur delectus. Lorem ipsum dolor sit amet, consectetur adipisicing elit. A blanditiis commodi consequuntur delectus doloremque error esse mollitia nemo neque nesciunt nihil odit, perferendis praesentium, suscipit velit veniam voluptate. Autem, soluta! Et odit, totam! Accusantium deserunt earum laudantium molestiae praesentium quam reiciendis ut? Beatae harum numquam quisquam quod recusandae rem soluta sunt veniam. Animi doloremque exercitationem magnam recusandae repellendus similique, voluptas!

Setup Initial Values of CSS Variables

Link to this section

First we’ll set up the variables and their initial values in the :root pseudo-selector:

:root {
  --background-color: #2eec96;
  --text-color: #222;
  --label-color: #1a905d;
  --border-color: rgba(250, 250, 250, 0.5);
  --base-font-size: 14px;
  --base-line-height: 1.6;
}

Write some styles using these variables. Think about using these values as a base and generating many of the other values in your styles from them. For example, set your body to use the --base-font-size variable and set all other font sizes in rems or ems. This will let you create fewer variables while still giving the user the power to affect the entire document.

body {
  font-size: var(--base-font-size);
  line-height: var(--base-line-height);
  color: var(--text-color);
  background-color: var(--background-color);
}

You may also want to add some transitions for the specific properties that will be changing to smooth out the theme editing experience.

Create Some Inputs

Link to this section

Now it’s time to create the form the user will be interacting with to customize their theme. I’m using color inputs for the color options and range inputs for the font size and line height.

<input
  type="color"
  id="background-color"
  value="#2eec96"
>
<input
  type="range"
  id="base-font-size"
  min="12"
  max="16"
  step="1"
  value="14"
>

Set your initial input values to match the starting values of your CSS variables. You could also set these through JavaScript on page load to remain DRY.

Set up Event Listeners

Link to this section

I’m setting up listeners to the “change” event for color inputs, and the “input” event for range inputs. I’ve extracted the logic to a separate function called handleInputChange so I can reuse it for all the inputs. A small complication is the need to set --base-font-size in pixels and everything else in unitless values. So handleInputChange accepts a second parameter that is a simple Boolean to handle this.

const handleInputChange = (property, pixels) => {
  document.documentElement.style.setProperty(
    `--${property}`,
    `${event.target.value}${pixels ? 'px' : ''}`
  );
};

document.querySelector('#background-color')
  .addEventListener('change', event => {
    handleInputChange('background-color', false);
  });

document.querySelector('#base-font-size')
  .addEventListener('input', event => {
    handleInputChange('base-font-size', true);
  });

That’s all it takes to set up basic interactive theming with CSS variables. We can go a step further and keep these values in localStorage so the theme settings will persist on refresh.

Store Values of CSS Variables in localStorage

Link to this section

I’ll edit my handleInputChange function to store values in localStorage:

const handleInputChange = (property, pixels) => {
  document.documentElement.style.setProperty(
    `--${property}`,
    `${event.target.value}${pixels ? 'px' : ''}`
  );
  localStorage.setItem(
    property,
    `${event.target.value}${pixels ? 'px' : ''}`
  );
};

Next, I’ll need to add some functionality to grab the values from localStorage and set them on page load. I will need to set the CSS variable and the input value so everything is consistent. Once again, I’ve extracted the logic to a separate function. And again, be careful of pixel values. This time, when setting the input values, the ‘px’ string will need to be removed.

const setValueFromLocalStorage = property => {
  let value = localStorage.getItem(property);
  document.documentElement.style.setProperty(
    `--${property}`,
    value
  );

  const input = document.querySelector(`#${property}`);
  input.value = value.replace('px', '');
}

document.addEventListener('DOMContentLoaded', () => {
  setValueFromLocalStorage('background-color');
});

Now, refresh your page to see a persistent theme!

Bonus: Pre-Defined Themes Using CSS Variables

Link to this section

If you want to offer some pre-defined themes, in addition to the free-form inputs, simply create a method that sets all the variables to pre-defined values and hook it up to an event listener.

I’ve created a setTheme function that accepts an options object, loops through the object keys, and sets the values. I’ve reworked the function that actually sets the values a bit so it’s more generic.

Now, I just need to call this function with the values I want to set on the appropriate trigger:

setTheme({
  'background-color': '#2eec96',
  'text-color': '#222222',
  'label-color': '#1a905d',
  'border-color': 'rgba(250, 250, 250, 0.5)'
});

CSS variables make live theming a piece of cake. Check out the live experiment here. In a real-world app, you would probably want to store the theme options in user settings on the back-end. Hopefully this gets you started playing around with the fun things that CSS variables make possible.

Go to experiment

Live Theming with CSS Variables

Click here to view the experiment on Codepen

You can learn more about CSS variables in my article here:

Go to article

Unlocking the Benefits of CSS Variables

More and more developers are starting to use CSS variables, or as they are more correctly known, custom properties. Native CSS custom properties…

More Articles

Go to article

Article published date April 9, 2020

System-Based Theming with Styled Components

Learn how to support system-based theming in Styled Components, while allowing a user to select their preferred theme and persist that choice.

Go to article

Article published date November 5, 2018

Implicit State Sharing in React & Vue

Learn to use React’s Context API and provide/inject in Vue to share state between related components without resorting to a global data store.

Go to article

Article published date August 6, 2018

Component Reusability in React & Vue

Learn how to use render props in React and scoped slots in Vue to create components that are flexible and reusable.

Go to article

Article published date March 11, 2018

What’s the Deal with Margin Collapse?

Learn about margin collapse, a fundamental concept of CSS layout. See visual examples of when margin collapse happens, and when it doesn’t.