This theme switcher system allows users to toggle between Light Mode, Dark Mode, and Auto Mode (which follows the system's color preference). Here's how to properly implement and use the required HTML attributes and classes.
To make the switcher work, each button (or clickable element) must include two attributes:
theme-role="toggle"
theme-label="light"
/ "dark"
/ "auto"
light
: Forces light themedark
: Forces dark themeauto
: Follows the user's operating system preferenceYou can assign these attributes to any HTML element (buttons, links, divs, etc.).
When a user selects a theme:
<body>
using a class (u-theme-light
or u-theme-dark
).theme-active
, allowing you to visually highlight the active choice.localStorage
, and the corresponding class is added to <body>
.Your CSS should include styling rules that correspond to two body classes:
.u-theme-light
β Applied when light theme is active.u-theme-dark
β Applied when dark theme is activeYou are free to design each theme however you like. The presence of these classes is what triggers the visual change.
To integrate this theme switcher:
theme-role="toggle"
to all theme buttons.theme-label
to define each buttonβs function (light
, dark
, or auto
)..u-theme-light
and .u-theme-dark
in your CSS for the visual appearance..theme-active
to highlight the selected option.<script>
// Detects if the user prefers dark mode at the system level
const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)");
/**
* Applies the selected theme to the <body> element
* @param {string} theme - "dark", "light", or "auto"
*/
function applyTheme(theme) {
const body = document.body;
// Remove any existing theme class
body.classList.remove("u-theme-dark", "u-theme-light");
if (theme === "dark") {
// Apply dark theme
body.classList.add("u-theme-dark");
} else if (theme === "light") {
// Apply light theme
body.classList.add("u-theme-light");
} else {
// Auto: follow the system's preferred color scheme
if (darkThemeMq.matches) {
body.classList.add("u-theme-dark");
} else {
body.classList.add("u-theme-light");
}
}
}
/**
* Sets the theme based on user preference saved in localStorage.
* If no preference is saved, auto mode is applied.
*/
function setTheme() {
// Remove the active class from all toggle buttons
document.querySelectorAll('[theme-role=toggle]').forEach(el => {
el.classList.remove("theme-active");
});
// Get the saved theme from localStorage
const savedTheme = localStorage.getItem("theme");
if (!savedTheme) {
// Default to auto mode if no theme is saved
document.querySelector("[theme-label=auto]")?.classList.add("theme-active");
applyTheme("auto");
} else {
// Apply the saved theme and mark the correct button as active
document.querySelector(`[theme-label="${savedTheme}"]`)?.classList.add("theme-active");
applyTheme(savedTheme);
}
}
// Add click event listeners to all theme toggle buttons
document.querySelectorAll('[theme-role=toggle]').forEach(button => {
button.addEventListener("click", function () {
const selectedTheme = this.getAttribute("theme-label");
if (selectedTheme === "auto") {
// Remove saved theme to fallback to system preference
localStorage.removeItem("theme");
} else {
// Save the selected theme
localStorage.setItem("theme", selectedTheme);
}
setTheme();
});
});
// Listen for system color scheme changes if "auto" mode is active
darkThemeMq.addEventListener("change", () => {
if (!localStorage.getItem("theme")) {
setTheme();
}
});
// Initialize the theme on page load
setTheme();
</script>