# 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, 🤣

%[https://twitter.com/okikio_dev/status/1407156597642641408?s=20]

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)

%[https://youtu.be/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 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,

%[https://youtu.be/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 may need to open the demo in a new tab for it to work properly.

%[https://codesandbox.io/s/mystifying-khayyam-48pl0?file=/index.html:2413-2516]

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 

%[https://twitter.com/okikio_dev/status/1415211214888738818?s=20]

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)
