I have a dark-mode
function that detects the system default appearance (light
, dark
, auto
) and changes the CSS
to match the selected system theme
. I also allow localStorage
to remember the last selected mode when the user browses different subpages on my site.
The problem is that my function only toggles between modes while the system default is set to light mode.
How do I update the code so that when the system is set to dark mode or auto, I am able to override and toggle light mode?
$(document).ready(function() {
if (localStorage.getItem("mode") == "dark-theme") {
$("body").addClass("dark-theme");
} else if (localStorage.getItem("mode") == "light-theme") {
$("body").removeClass("dark-theme");
}
var mq = window.matchMedia("(prefers-color-scheme: dark)");
if (localStorage.getItem("mode") == "light-theme") {
$("body").removeClass("dark-theme");
} else if (mq.matches) {
$("body").addClass("dark-theme");
}
});
$("#theme_toggle").on("click", function() {
if ($("body").hasClass("dark-theme")) {
$("body").removeClass("dark-theme");
localStorage.setItem("mode", "light-theme");
} else {
$("body").addClass("dark-theme");
localStorage.setItem("mode", "dark-theme");
}
});
body {
--font-color: blue;
--bg-color: white;
}
body.dark-theme {
--font-color: white;
--bg-color: black;
}
@media (prefers-color-scheme: dark) {
body {
--font-color: white;
--bg-color: black;
}
body.light-theme {
--font-color: blue;
--bg-color: white;
}
}
body {
color: var(--font-color);
background: var(--bg-color);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<label for="theme_toggle">
<input type="checkbox" id="theme_toggle">
Dark mode?
</label>
<h3>Title</h3>
Try like this:
$(document).ready(function() {
//check for localStorage, add as browser preference if missing
if (!localStorage.getItem("mode")) {
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
localStorage.setItem("mode", "dark-theme");
} else {
localStorage.setItem("mode", "light-theme");
}
}
//set interface to match localStorage
if (localStorage.getItem("mode") == "dark-theme") {
$("body").addClass("dark-theme");
$("body").removeClass("light-theme");
document.getElementById("theme_toggle").checked = true;
} else {
$("body").removeClass("dark-theme");
$("body").addClass("light-theme");
document.getElementById("theme_toggle").checked = false;
}
//add toggle
$("#theme_toggle").on("click", function() {
if ($("body").hasClass("dark-theme")) {
$("body").removeClass("dark-theme");
$("body").addClass("light-theme");
localStorage.setItem("mode", "light-theme");
} else {
$("body").addClass("dark-theme");
$("body").removeClass("light-theme");
localStorage.setItem("mode", "dark-theme");
}
});
});
body {
--font-color: blue;
--bg-color: white;
}
body.dark-theme {
--font-color: white;
--bg-color: black;
}
@media (prefers-color-scheme: dark) {
body {
--font-color: white;
--bg-color: black;
}
body.light-theme {
--font-color: blue;
--bg-color: white;
}
}
body {
color: var(--font-color);
background: var(--bg-color);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<label for="theme_toggle">
<input type="checkbox" id="theme_toggle">
Dark mode?
</label>
<h3>Title</h3>
The reason the code didn't work as expected was because the second if
statement was overwriting the result of the first.
It's simpler to break this down into checking for the localStorage first and then using that to set the user interface (including the checkbox).
If localStorage is missing (e.g. the user is visiting for the first time or has cleared localStorage) then set it to be the browser preference. The typical approach is to assume that anything other than dark, means use light.
Toggling a .light-theme
class in the code allows the CSS to apply the browser preference if the user has scripting disabled (e.g. browser says default dark mode, code has not run so there is no .light-theme
--> use dark mode).