Over the years, CSS has evolved to cater to the ever-growing needs and flexibility requirements of modern UI design. The demand for highly customizable applications is rapidly increasing, and with that the need for developers who can create such websites.
Theming is the ability to style various aspects of our website differently depending on a context, while still maintaining visual appeal. It could be as simple as changing colors and backgrounds or font-sizes and icons. In CSS, we can achieve theming by piecing together various CSS variables (props) in a context (e.g, black and white) to enable better presentation of an application.
In this tutorial, we’ll cover how to develop apps that are theme-aware using CSS variables. We’ll also learn how to use CSS variables and JavaScript to customize websites and applications.
A CSS variable (also called a custom property) is a variable you assign a CSS value to and reuse throughout your application.
In older versions of CSS, it’s common to define different values for CSS properties and apply them repeatedly whereever they are needed. In the example below, the a
element is set to black (#000), and although other elements (p) will also need to use the same color value (#000), you need to explicitly type out the color value (#000) every time you want to use it on another element.
a { color: #000; } p { color: #000; }
With CSS variables, we only need to define the value once and then reference it wherever needed. It is identical to defining a variable in any programming language. In the example below, we declare a variable black at the top level of our CSS document and then reference it wherever it is needed.
:root { --black: #000; } a { color: var(--black); } p { color: var(--black); }
N/B: When declaring CSS variables, the syntax is
**--**
, followed by the name of the variable. To reference it, we callvar(--variable-name)
on the property we wish to assign it to.
CSS variables can be scoped to certain components in an application and can be overridden in inner components when necessary.
This is one feature that makes CSS variables stand out, and it’s also what makes it perfect for creating theme0aware websites. In the example below, the inner div and outer div have been defined with two different background colors. First, we define --some-color
at .outer-div
by setting its value to black. Then we redefine it at .inner-div
by setting it’s value to white, causing it to override its value under this element.
The first .text-color
gets the value that was defined at .outer-div
because that is its parent, while the second .text-color
div gets it from .inner-div
as that is the direct child of that div.
With the illustrations above, all that’s left to make a theme-able website is to figure out a way to swap the property values of various elements based on the current context.
To better understand the concept, let’s talk about black and white themes. At a very basic level, to support black and white themes on a website, the background and text color will have to alternate to maintain readability. So, when we are viewing the black theme, the text color should be white, and when we are viewing the white theme, the text color should be black.
The following pen shows what the website looks like on a white theme:
The following pen shows what the website looks like on a black theme:
Typically, on a website, we may have a toggle that enables us to switch between the available themes. To change the CSS variables in real-time, we need to add a little bit of JavaScript to control the process.
Let’s create a page with dark and light themes and add some JavaScript so that it switches whenever the checkbox is clicked:
In the CSS above, we define two site-wide color sets:
:root { --primary-color: #302AE6; --secondary-color: #536390; --font-color: #424242; --bg-color: #fff; --heading-color: #292922; } AND [data-theme="dark"] { --primary-color: #9A97F3; --secondary-color: #818cab; --font-color: #e1e1ff; --bg-color: #161625; --heading-color: #818cab; }
The root contains the default colors needed for the light theme, and “data-theme=dark”
contains all the colors needed for a dark theme on various parts of our website.
We’ve also added a toggle box to be used to swap out themes whenever a user clicks.
Next, we add some JavaScript to make the swap possible:
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); function switchTheme(e) { if (e.target.checked) { document.documentElement.setAttribute('data-theme', 'dark'); } else { document.documentElement.setAttribute('data-theme', 'light'); } } toggleSwitch.addEventListener('change', switchTheme, false);
Here’s how we achieved this:
A good thing to have on a themeable website is the ability to store the user preference, so the next time the user visits the website it automatically loads up in that context. To achieve that, we will make use of local storage.
Add the following lines to the snippet above:
if (e.target.checked) { document.documentElement.setAttribute('data-theme', 'dark'); localStorage.setItem('theme', 'dark'); //this will be set to dark } else { document.documentElement.setAttribute('data-theme', 'light'); localStorage.setItem('theme', 'light'); //this will be set to light }
Now that we are saving the user preference, we need to check for it every time a site loads so we know which version to display to the user. Add this snippet at the top level in the Javascript document:
const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; if (currentTheme) { document.documentElement.setAttribute('data-theme', currentTheme); if (currentTheme === 'dark') { toggleSwitch.checked = true; } }
Here, we check if a theme preference has been stored in local storage. Based on the value stored, we toggle the box (by setting it to true or false) to swap the needed variables.
In this tutorial, we learned about theming and how to create a basic version of a theme-able website using CSS variables. We also learned about variable inheritance and local scoping alongside how to make it all come together using basic JavaScript. To learn more about CSS theming check out the official docs here.
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Vite is a versatile, fast, lightweight build tool with an exceptional DX. Let’s explore when and why you should adopt Vite in your projects.
Explore advanced capabilities for content sharing with the navigator.share
API, including dynamic content sharing, custom share targets, and batch sharing.
We spoke with Chas to get his insights on building technology and internal processes for companies that are scaling quickly.
Cypress is one of today’s foremost tools for testing web applications. Let’s explore when and why you should adopt Cypress in your projects.
5 Replies to "A guide to theming in CSS"
This is a very cool overview of a simple way to implement. Thanks for sharing.
Thanks a lot for this amazing article. Before this article theming concept was just a nightmare for me. But Man u nailed it.
Thanks for the great article! A couple confusing parts:
– You write “ N/B: When declaring CSS variables, the syntax is **–**”… I would remove the asterisks as it’s unclear for beginners if asterisks are actually part of the syntax of your custom way of highlighting the double dash
– “ perfect for creating theme0aware websites” the 0 should be a hyphen
Thanks for pointing that out, just updated
Thank you for this very informative post! I have to admit, however, that I spent quite a few moments trying to decipher your code for the white text/black text example. It was confusing because the ‘white text’ is actually black and visa-versa. I had my eyes crossing before I realized that. Perhaps you should change the white text to say “Black Text” (since it is) or simply take away the ‘Text’ part of the string. But great content!