PSA: Add dark mode to your sites, or at least let the browsers do it for you
Tags: javascript, css, userexperience, webdev
Published: 2021-06-22 06:04:11

---
title: PSA: Add dark mode to your sites, or at least let the browsers do it for you
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fr59zqbzfdfiqljtekxc.jpg
published: true
date: 2021-06-22 05:47:18 UTC
tags: JavaScript,CSS,userexperience,webdev
canonical_url: https://blog.okikio.dev/psa-add-dark-mode-to-your-sites-or-at-least-let-the-browsers-do-it-for-you
---

I have a simple message for web developers, start adding the `color-scheme` property to your webpages.

```html
<!--
  The page supports both dark and light color schemes,
  and the page author prefers dark.
-->
<meta name="color-scheme" content="dark light">

```

or you can even add it using css

```css
/*
  The page supports both dark and light color schemes,
  and the page author prefers dark.
*/
:root {
  color-scheme: dark light;
}

```

I absolutely detest sites that _"have a dark mode, **BUT DON'T MAKE THE SCROLLBAR DARK!**"_, a great example of this is [docusaurus](https://docusaurus.io/).

![docusaurus-scrollbar](https://cdn.hashnode.com/res/hashnode/image/upload/v1624330996244/OTPgvYJHa.png)

Docusarus Why???

![burning-eyes](https://media.giphy.com/media/KGbqQQoVN43cY/giphy.gif)

I actually tweeted at them asking why the scrollbar isn't dark as well, 🤣

{% twitter 1407156597642641408 %}

The light mode scrollbar hurts the eyes, and ruins the look of the site, so, for the sake of everyone who has eyes and likes dark mode, please use `color-scheme`, you can even use it together with your dark mode toggle by using css, for example, one of the sites I made for a client [josephojo.com](https://josephojo.com)

{% youtube lLRPLPSppzA %}

When using the `color-scheme` property you can turn form elements, webpage background, text color, and scrollbars dark, a more famous example would be, [github](https://github.com/okikio/native/blob/master/packages/animate/README.md),

![github-scrollbar](https://cdn.hashnode.com/res/hashnode/image/upload/v1624323074476/aXweAKUtW.png)

Notice, how the scrollbar is dark, and doesn't burn the eyes, they're able to do it, by using the meta tag.

![github dark mode](https://cdn.hashnode.com/res/hashnode/image/upload/v1624326485815/AfJIHRJ-M.png)

For [josephojo.com](josephojo.com) I used the `color-scheme` css property together with `@media (prefers-color-scheme: dark) {}` and the `.dark` class, the final result is

```css
html {
    color-scheme: light;
}

html.dark {
    color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
    html:not([data-theme]) {
        color-scheme: dark;
    }
}

```

When creating the site I used [tailwindcss](https://tailwindcss.com/), with the dark mode set to "class", my tailwind config looked like this,

```js
module.exports = {
    darkMode: 'class',
    // ...
}

```

For those who haven't used `tailwindcss` before, it's basically the same as defining a class that when added to the html element will signal that the site is in dark mode.

Or in simpler terms it's,

```html
<html class="dark"> 
    <!-- ... -->
</html>

```

> **You:** Wait, but, how did you handle the theme toggle button?
> 
> **Me:** I'm glad you asked.

Now that we have some boilerplate code, all you really need to do, is setup a toggle that will remember our current theme state.

While developing josephojo.com, I found that you have to set your theming system to support the native media theme before anything else, it's generally less painful to the user, that's why I set `html:not([data-theme])` in the `prefers-color-scheme: dark` media query,

```css
/* ... */
@media (prefers-color-scheme: dark) {
    html:not([data-theme]) {
        color-scheme: dark;
    }
}
/* ... */

```

`html.dark` represents the dark theme applied by `tailwind` and `[data-theme]` represents the currently applied theme, if `data-theme` is different from the local storage, then the theme was manually toggled and the page should use the new theme in `data-theme` as well as update the local storage theme, otherwise, it should use the local storage theme as `data-theme`, but because `data-theme` is only applied to the `html` element after javascript is loaded we can tell our css to use the default dark theme if `prefers-color-scheme: dark` and the html element doesn't have the `data-theme` attribute.

The result you get is this,

{% youtube gVm6E186XWo %}

As you saw at the end there, changing the actual browser theme won't permanently change the theme set in local storage, with the idea being, if a user a manually changes the theme they must want to use that theme permanently, otherwise use the system theme.

Here is the code for the theme toggle,

```js
// Based on [joshwcomeau.com/gatsby/dark-mode/]
let getSavedTheme = () => {
  const theme = window.localStorage.getItem("theme");
  // If the user has explicitly chosen light or dark,
  // let's use it. Otherwise, this value will be null.
  if (typeof theme === "string") return theme;

  // If they are using a browser/OS that doesn't support
  // color themes, let's not do anything.
  return null;
};

let saveTheme = (theme) => {
  // If the user has explicitly chosen light or dark, store the default theme
  if (typeof theme === "string")
    window.localStorage.setItem("theme", theme);
};

let mediaTheme = () => {
  // If they haven't been explicitly set, let's check the media query
  const mql = matchMedia("(prefers-color-scheme: dark)");
  const hasMediaQueryPreference = typeof mql.matches === "boolean";
  if (hasMediaQueryPreference) return mql.matches ? "dark" : "light";
};

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

// Get theme from html tag, if it has a theme or get it from localStorage
let checkCurrentTheme = () => {
  let themeAttr = html.getAttribute("data-theme");
  if (themeAttr) return themeAttr;

  return getSavedTheme();
};

// Set theme in localStorage, as well as in the html tag
let applyTheme = (theme) => {
  html.className = theme;
  html.setAttribute("data-theme", theme);
};

try {
  // if there is a saved theme in local storage use that,
  // otherwise use `prefer-color-scheme` to set the theme 
  let theme = getSavedTheme();
  if (theme == null) theme = mediaTheme();

  // set the initial theme
  html.setAttribute("data-theme", theme);
  html.classList.add(theme);

  // If a user changes the system/browser/OS theme, update the site theme as well,
  // but don't save the change in local storage
  window
    .matchMedia("(prefers-color-scheme: dark)")
    .addEventListener("change", (e) => {
      applyTheme(e.matches ? "dark" : "light");
    });

  // On theme toggle button click, toggle the page theme between dark and light mode,
  // then save the theme in local storage
  document
    .querySelector("#theme-toggle")
    .addEventListener("click", () => {
      let theme = checkCurrentTheme() === "dark" ? "light" : "dark";
      applyTheme(theme);
      saveTheme(theme);
    });
} catch (e) {
  console.warn("Theming isn't available on this browser.", e);
}

```

You can view the demo below, but you need to open the demo in a new tab for it to work properly ( **note** , I don't mean open the entire Code Sandbox in a new tab, I specifically mean the demo. CodeSandbox has disabled local storage, when the demo is attached to rest of the CodeSandbox instance, so, you need to detach the demo from the CodeSandbox instance).

{% codesandbox mystifying-khayyam-48pl0 %}

Also, notice, how I never set the text color, background color, scrollbar color or button styles, that's part of the magic of setting `color-scheme`.

You can read more about `color-scheme` on [web.dev](https://web.dev/color-scheme/)

Please tell me what you think about `color-scheme` in the comments below.

**Update:** Another cool part feature of the `color-scheme` meta tag is that Samsung Internet won't force dark mode on your site if it uses the `color-scheme` meta tag, from what I can tell Chrome might implement a similar feature in the future. I tweeted about it 

{% twitter 1415211214888738818 %}

You can learn more about this on the Samsung Developers site, 

https://developer.samsung.com/internet/blog/en-us/2020/12/15/dark-mode-in-samsung-internet

* * *

Photo by [Alexander Andrews](https://unsplash.com/@alex_andrews) on [Unsplash](https://unsplash.com/photos/vGCErDhrc3E)