skip navigation

Dark Mode CSS Only

This theme switcher functions without using JavaScript. However you do need some JS to make it persistent across multiple pages and user visits. Typically this is done by setting a localStorage value when the theme is changed and then checking that value on every page load.

Prerequisites

There are now multiple ways to set up dark themes. This will use the color-scheme property in the :root element along with the light-dark() function for setting colours.

For example:

:root {
    color-scheme: light dark;
    --text: light-dark(#222, #ccc);
}

The HTML element

The HTML can be done different ways. Here we’ll use the <select> element. The default value will be system and because there’s no CSS set for this value it won’t do anything. So the user’s system settings will be used, either light or dark, until they choose something else.

<select name="theme" id="theme">
    <option value="system">system</option>
    <option value="dark">dark</option>
    <option value="light">light</option>
</select>

The CSS

This sets the :root element color-scheme value based on a descendant selector #theme [value="dark"]:checked. The ID #theme is our <select> element. And [value="dark"]:checked is an attribute selector within #theme.

:root:has(#theme [value="dark"]:checked) {
  color-scheme: dark;
}

:root:has(#theme [value="light"]:checked) {
  color-scheme: light;
}

To make the transitions smooth:

body {
	transition: all 0.4s ease-out;
}

The Javascript

The JavaScript is fairly simple:

// grab the select element
const select = document.querySelector('select');

// Listen for a change on the select element
// Record the change to localStorage
select.addEventListener('change', () => {
    localStorage.setItem('theme', select.value);
})

// Create a function to check the localStorage setting
// Set the select element to that value
function checkTheme () {
    const theme = localStorage.getItem('theme');
    select.value = theme;
}

// Run the function when the page loads
checkTheme();