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!
Setup Initial Values of CSS Variables
Link to this sectionFirst 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 sectionNow 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 sectionI’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 sectionI’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 sectionIf 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)'
});
Conclusion
Link to this sectionCSS 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.
Live Theming with CSS Variables
Click here to view the experiment on Codepen
You can learn more about CSS variables in my article here:
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…