CLICK
← Docs

Creating a darkmode switch toggle for web

Used technologies:

  • Bootstrap v5.2+
  • (Also possible without Bootstrap, as long as you use CSS variables)

The idea

Toggling all colors for a website using minimal (code-)changes and retaining a appealing visualisation.
If you know CSS variables you know the power of variables in CSS. Being able to re-use those variables or overwrite them in bulk where needed. For example, creating root-variables and changes those for a dark view of the same website.

First step: Use CSS variables

As easy/stupid as it sounds, most websites or libraries don't (yet) include this out of the box. Make sure every color you see is using a CSS variable. Think of heading color, background colors and all shades/gradients in between.
Utility classes for example commonly don't use CSS variables.
If you are using Bootstrap 5 then it mostly has all it's components using CSS variables already.

:root {
  // Theme colors
  --primary: #40531b;
  --background: #ecede7;

  // Grays
  --gray-800: #181818;
  --gray-600: #6b6b6b;
  --gray-300: #e2e2e2;
  --gray-100: #f5f5f5;

  // Root and body CSS variables
  --body-color: #{$body-color};
  --body-bg: #{$body-bg};
}

Detecting color scheme

We need to know the color scheme set in the user's browser/system. This is easily done using CSS with the `@media` prefers-color-scheme option:

@media (prefers-color-scheme: dark) {
That is great for changing the root CSS variables using only CSS!

But, if you want to create a clickable 'switch' it has to be toggleable with Javascript. Luckily this color scheme is also detectable with Javascript and we will set this on a data attribute for the <html> wrapper. From there, we can override the :root variables as well.
Pasting the following script as high in the <head> as possible the scheme will be set before page rendering.

<script>
  if (
      localStorage.getItem("colormode") === "dark" ||
      (window.matchMedia("(prefers-color-scheme: dark)").matches &&
      !localStorage.getItem("colormode"))
  ) {
      document.documentElement.dataset.colormode = "dark";
  }
</script>

Like so (when loading):

<html data-colormode="dark" lang="..." style="...">

As you may see, the 'light' mode won't be set since the default :root CSS variables are defined in light scheme.
Also, we check a local cookie of the colormode has already been set there. More about this cookie in the next step.

Switching color scheme

If you are only using CSS this is doable by emulating it in the devTools. Also you can adjust your system or browser settings.

For switching it with Javascript we will create a button which will toggle it. Clicking this button must change the HTML data-colormode and settings a cookie to remember the scheme set if the website is reloaded.

<button class="mydarkmodebutton">Toggle darkmode</button>

... 

<script>
  if (window.CSS && CSS.supports('color', 'var(--body-color)')) {
    document.querySelector('.mydarkmodebutton').addEventListener('click', function(e) {
      darkmode = document.documentElement.dataset.colormode == 'light' ? 'dark' : 'light';
      document.documentElement.dataset.colormode = darkmode; // set html dataset
      localStorage.setItem('colormode', darkmode); // set local cookie
      return;
    });
  } else { // CSS vars are not supported so hide it..
    document.querySelector('.mydarkmodebutton').style.display = 'none';
  }
</script>

Creating the darkmode view

Now the html element represents our current color scheme we can override the :root CSS variables using the following line:

:root[data-colormode="dark"] {

All previous set CSS variables will now have a different value representing the dark version, for example:

:root[data-colormode="dark"] {
  // Theme colors
  --primary: #283310;
  --background: #1d1d1c;

  // Grays (inversed)
  --gray-800: #f5f5f5;
  --gray-600: #e2e2e2;
  --gray-300: #6b6b6b;
  --gray-100: #181818;

  // Root and body CSS variables
  --body-color: var(--gray-600);
  --body-bg: var(--background);
}

Done!

See my version in action by clicking the sun/moon icon:





Go the extra mile..

For example, lightly hide those bright images in darkmode or change the meta theme-color value:

<style>
  [data-colormode="dark"] img {
    opacity: 0.90;
    transition: opacity 0.3s ease;
  }
  
  [data-colormode="dark"] img:hover,
  [data-colormode="dark"] img:focus {
    opacity: 0.95;
  }
</style>

<script>
  var colorhex = (document.documentElement.dataset.colormode == 'light' ? '#ecede7' : '#1d1d1c');
  document.querySelectorAll('[name="theme-color"], [name="msapplication-TileColor"]').forEach((e) => e.setAttribute('content', colorhex));
</script>


Contact_

Let's get in touch

And we'll see from there

Collaboration?
Start a project?
You name it...


Contact me at

hello@luukthe.dev