Switching themes with pure JavaScript in 30 lines

Anton Mordik
3 min readJan 24, 2021

Hi everyone, today I would like to talk about one simple approach to implementing themes in a web application using pure JS and CSS variables. In the title, I mentioned 30 lines, yes, this is not a little, no one bothers to do everything in one line as most javascriptors like, but in the article we will limit ourselves to a more readable version😸. First, let’s create a file themes.js with following content:

// themes.js
const themes = {
light: {
primary: '#3A86FF',
secondary: '#2C365E',
background: '#FEF9EF'
},
dark: {
primary: '#FB5607',
secondary: '#FEF9EF',
background: '#2C365E'
}
};
const DEFAULT_THEME = Object.keys(themes)[0];

Here I set up a config with two themes, for each theme I chose 3 colors and set a default theme. This is a very convenient solution to keep theme variables in a separate place in JavaScript for further use in various cases. Next, let’s write a function to turn this themes into CSS variables.

// theme.js
// ...
function getThemeVariables(config) {
return `:root{ ${
Object.entries(config)
.map(([name, value]) => `--${name}: ${value};`)
.join(' ')
} }`;
}

This piece of code applies variables to the :root selector, which refers the root element of the document — html.

CSS variables inside browser devtools
CSS variables

Now we need to put these styles in the head of the document. To do this, we will write another function and immediately call it for execution synchronously during document loading.

// theme.js
// ...
setup();function setup() {
let theme = localStorage.getItem('theme');
if (!Object.keys(themes).includes(theme)) {
theme = DEFAULT_THEME;
}
const defs = document.createElement('style');
// Adding a unique role for further selection
defs.setAttribute('role', 'theme-colors');
defs.innerHTML = getThemeVariables(themes[theme]);
document.head.appendChild(defs);
}

This creates a stylesheet that is added to the head of the document during page load. Also, a unique attribute role="theme-colors" is defined for the element, for further easier selection and changing variables inside it.

Now let’s include the script in our html file. In fact, there is no difference in what place to connect, but we will connect it in case of good manners before any mention of styles appears, either a tag style or link rel="stylesheet".

<title>Theme switching example</title>
<script src="./themes.js"></script>
<style>
/* This section of styles will be added after the variable definitions */
</style>

And now we can use variables inside CSS like:

body {
background-color: var(--background);
}
h1 {
color: var(--primary);
}

Well, to demonstrate switching themes, I added a simple control to the document body and connected a script to interact with it:

<input type="checkbox" id="switcher">
<label for="switcher">Switch for dark mode</label>
<script src="./switch.js"></script>

And the script code:

// switch.jsconst switcher = document.getElementById('switcher');
// Getting style section by a unique role
const styles = document.querySelector('[role="theme-colors"]');
switcher.addEventListener('change', (e) => {
const theme = e.currentTarget.checked ? 'dark' : 'light';
localStorage.setItem('theme', theme);
styles.innerHTML = getThemeVariables(themes[theme]);
});
Theme switching demo

This is my first article on medium and I would be glad to any constructive critique. You can find me on GitHub or LinkedIn. Thanks for your attention.

Link for the repository https://github.com/antonmordik/pure-js-theme-switch and live demo https://antonmordik.github.io/pure-js-theme-switch/

--

--

Anton Mordik
0 Followers

JavaScript enthusiast from Minsk