After defining the basic theme colors for my website, I thought about adding a dark mode, as it shouldn’t be too hard to do using SCSS. Amazingly, thanks to CSS getting new features such as variables, this was even easier than I thought, the only real reason to use SCSS here is because I want the site to display dark/light mode depending on system settings without JavaScript, and SCSS makes it easier to bundle all the theme’s styles in one place.
Defining the Theme §
We define the themes using mixins. Most of the rules in these mixins
are colors, however, I have also defined some extra rules, such as
inverting the colors of the tikzpictures in dark mode to ensure they
are readable on a dark background. It also includes a rule for a class
.theme-icon
, for use in a toggle switch to switch the theme (it
looks like this: ). I have also adjusted
a bunch of colors in the syntax highlighting to ensure that the source
code blocks are readable using mostly the same colors in dark and
light mode.
@mixin light-theme {
--text_color: #000;
--body_background_color: #ffeeff;
--source_background_color: #ffeeee;
--footer_color: #555;
--highlight_color: #707;
--secondary_color: #a10;
.tikzpicture {
filter: none;
}.theme-icon::after {
content: "🌙"
}
}
@mixin dark-theme {
--text_color: #fff;
--body_background_color: #000022;
--source_background_color: #110000;
--footer_color: #aaa;
--highlight_color: #a0f;
--secondary_color: #e40;
.tikzpicture {
filter: invert(1);
}.theme-icon:after {
content: "☀️"
} }
Implementing Theme Switching §
We will implement the theme switching using the techniques outlined in
[Adhu24]. The following CSS code ensures the theme is set up to
match the system theme, but can be overriden by giving the body
element the .dark_mode
or .light_mode
class using JavaScript.
:root {
@include light-theme;
}
.dark_mode {
@include dark-theme;
}
@media (prefers-color-scheme: dark) {
:root {
@include dark-theme;
}.light_mode {
@include light-theme;
} }
Toggling Themes §
Toggling themes is done using a small piece of JavaScript. We store the user’s theme selection in local storage, and retrieve it when loading the page to setup the user’s theme choice correctly. We also set the body class to the correct theme if there is no theme stored in local storage, but there is a system preference.
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
const userTheme = localStorage.getItem("theme");
if (userTheme == "dark") {
document.body.classList.add("dark_mode");
else if (userTheme == "light") {
} document.body.classList.add("light_mode");
else if (prefersDarkScheme.matches) {
} document.body.classList.add("dark_mode");
else {
} document.body.classList.add("light_mode");
}
Now, we get to the part of actually implementing the toggler. We do this from the ground up in JavaScript, so the toggle is not displayed if it doesn’t do anything due to lack of JavaScript.
const toggle = document.createElement("a");
.id = "toggle-button";
toggle.href = "javascript:;";
toggle.title = "Toggle Light/Dark Mode";
toggleconst icon = document.createElement("span");
.classList.add("theme-icon");
icon.appendChild(icon);
toggledocument.getElementById("nav").appendChild(toggle);
Now comes the actual toggling logic, it just toggles the body’s css classes and stores whichever class is active into local storage to be able to retrieve it later.
function toggle_scheme () {
let theme1 = "light";
let theme2 = "dark";
document.body.classList.toggle(theme1+"_mode");
document.body.classList.toggle(theme2+"_mode");
let theme = document.body.classList.contains(theme1 + "_mode") ? theme1 : theme2;
.setItem("theme", theme);
localStorage
}.addEventListener("click", function () {
toggletoggle_scheme();
; })