Dark and light mode with light-dark()
10.12.2024
As you know, the main purpose of your own website is to try out new things. Dark and light modes are of course not new, but so far I haven't had the pleasure of trying out the light-dark() CSS function. I will do so now.
To enable support for thelight-dark()
color function, the color-scheme must have a value oflight dark
, usually set on the:root
pseudo-class.
Alright, so let's start with some CSS …
:root {
color-scheme: light dark;
}
Instead of setting a single value for color
or background-color
we can now use the light-dark()
function and define one color for each mode. As defined in the :root
Element, the first one is for light and the second one for dark mode. Of course you can use custom properties as well, but to keep the example more readable, let's stick to hex codes.
body {
background-color: light-dark(#fff, #000);
color: light-dark(#000, #fff);
}
That's it?!
Now we have a working light and dark mode, based on the system preferences of the user. But if we want the user to switch between modes, we have to go some extra steps.
At first we need some buttons which can be used to change the between light and dark mode. I also add one to return back to system default. Each button has a js-
class, which we will use to identify the buttons with JavaScript. In addition we add a data-attribute which holds the value of the selected mode.
<button class="js-color-scheme-button" data-color-scheme="light">Light Mode</button>
<button class="js-color-scheme-button" data-color-scheme="dark">Dark Mode</button>
<button class="js-color-scheme-button" data-color-scheme="">System Default</button>
document.addEventListener('DOMContentLoaded', (event) => {
// find the all the buttons based on the js- class
const colorSchemeButtons = document.querySelectorAll('.js-color-scheme-button');
// get previously selected color scheme from localStorage
const colorScheme = localStorage.getItem('colorScheme') || '';
// find active button based on color scheme
const activeButton = document.querySelector('.js-color-scheme-button[data-color-scheme="' + colorScheme + '"]');
if (activeButton) {
// add css class to active button to apply different styles via css
activeButton.classList.add('is-active');
}
if (colorSchemeButtons) {
// do the same thing for all buttons
colorSchemeButtons.forEach(button => {
// listen to the click event (which includes touch as well)
button.addEventListener('click', event => {
// get the mode value from the data attribute
const colorScheme = button.dataset.colorScheme ?? '';
// remove active class from every button
colorSchemeButtons.forEach(btn => {
btn.classList.remove('is-active');
})
// and add it to the selected one
button.classList.add('is-active');
// add selected color scheme it to the html element
document.documentElement.setAttribute('data-color-scheme', colorScheme);
// save the value to local storage to keep the selection on page reload
localStorage.setItem('colorScheme', colorScheme);
})
})
}
})
To activate the different CSS styles based on the selected color scheme, we have to apply it based on the data attribute of the html
element, which we just changed via JavaScript. Now the browser knows which mode is set and which of the two values from the light-dark()
method is to be used.
/* force light mode, if activated by user (via JavaScript) */
html[data-color-scheme="light"] {
color-scheme: light;
}
/* force dark mode, if activated by user (via JavaScript) */
html[data-color-scheme="dark"] {
color-scheme: dark;
}
Flickering colors?
If you put your Javascript at the end of your HTML document you might notice flickering colors as soon as the selected value in localStorage differs from your system color scheme. This is because the website gets loaded with your system preference and in the end the JavaScript kicks in and changes all the colors. To prevent this, you can add two additional lines of JavaScript to the <head>
section of your page. This changes the data-attribute to the one saved to localStorage bevor the CSS is applied.
<!-- additional script for <head> section to prevent flickering -->
<script>
const colorScheme = localStorage.getItem('colorScheme') || '';
document.documentElement.setAttribute('data-color-scheme', colorScheme);
</script>
Sources
- light-dark() (MDN)
- data-Attributes (MDN)
PHP HTML CSS JS
What do you think?
Let's discuss on Mastodon