Implement dark mode on web with user override option

Dark mode is all the rage these days. There are a large set of people who prefer dark mode and it is believed dark mode is easier on the eyes. Cosmetic preferences aside dark mode also consume lesser power on devices with AMOLED screens. So implementing dark mode should give better battery backup to the user.

Almost all the major Operating systems have implements dark mode and the large set of native apps are respecting OS color scheme. But on the web story is a bit different, Most websites still do not respect user preferred color scheme.

Let's see how we can respect users color scheme preference and also override scheme.
(If user override is not needed skip to bottom)

Detect User preferred color scheme #

Latest browsers support prefers-color-scheme media query to detect user-preferred color scheme. As we will give user a option to override this value, we have to use JavaScript to execute if media query matches.

const defaultMode = "dark"; // if prefer scheme is not available

function detectColorMode() {
  if (
    window.matchMedia &&
    window.matchMedia("(prefers-color-scheme: dark)").matches
  ) {
    return "dark";
  } else if (window.matchMedia) {
    return "light";
  }
  return defaultMode;
}

Set color mode as classname #

After detecting user preferred mode, we are going to set color-scheme as body class name.

const body = document.querySelector("body");

function setColorMode(mode) {
  body.classList.add(mode);
}

Override OS scheme #

To overwrite system-wide color scheme, We will use a button and on click of button body classname will be toggled between dark and light.

const toggle = document.querySelector(".toggle-theme");
toggle.addEventListener("click", toggleClass);

function toggleClass() {
  body.classList.toggle("dark");
  body.classList.toggle("light");
}

Save overwritten scheme and pick if saved #

If user has overriden the color-scheme we will store the value to localstorage so that next time user visits website web can use saved preference.

function checkSavedMode() {
  return localStorage.getItem("color-mode");
}

function saveColorMode(value) {
  localStorage.setItem("color-mode", value);
}

const mode = checkSavedMode() ? checkSavedMode() : detectColorMode();
setColorMode(mode);

function toggleClass() {
  body.classList.toggle("dark");
  body.classList.toggle("light");
  body.classList.contains("dark")
    ? saveColorMode("dark")
    : saveColorMode("light");
}

Implement Dark and Light mode using CSS variables #

Now comes the part where we will write styles for dark and light modes. For that, we are going to use CSS variables.

In CSS variable we can define a variable by using -- prefix and use the value by using var(variablename). Inside these classes define all the variables which you want to have different values for dark and light mode. First one is background-color, Dark mode of course will have a darker background and light mode will have a lighter color. Similar way define all values like fontcolor, accent color, Borders and shadow.

.dark {
  --background-color: #333;
  --font-color: #f1f1f1;
  --primary-color: #ffbdbd;
}
.light {
  --background-color: #f1f1f1;
  --font-color: #111;
  --primary-color: #ffbdbd;
}

Use variables to customize elements #

Now use the variables defined in last step to build elements.

body {
  background: var(--background-color);
}
h1,
h2,
h3,
h4,
h5,
h6,
p,
input,
select,
pre,
code,
label {
  color: var(--font-color);
}
button,
input,
select {
  background-color: var(--background-color);
}
button {
  background-color: var(--primary-color);
}
.toggle-theme {
  position: absolute;
  width: 30px;
  height: 30px;
  right: 10px;
  top: 10px;
  background-color: transparent;
  border: 0;
  font-size: 1.2em;
}
.dark .toggle-theme::after {
  content: "☀";
  color: #f1f1f1;
}
.light .toggle-theme::after {
  content: "☽";
  color: #000;
}

Full code #

You can check the full working demo here

Color scheme without User override #


Since you've made it this far, sharing this article on your favorite social media network would be highly appreciated ! For feedback, please ping me on Twitter.

Published