Adding Dark Mode to my website using CSS Variables in 10 minutes
3 min read


Categories

Tags




Hatem AbdelMowgoud Hassan
Hatem AbdelMowgoud Hassan

SinceI didn’t update my portfolio for a while now, I decided to add something to it.

Dark Mode has been gaining lots of popularity lately, especially after MacOS implemented it natively and every application is trying to do the switch now.

Hatem Hassan Portfolio
Current design

When I thought about building my portfolio from scratch a year ago, I chose to write it in Vanilla JS and CSS so it would be super lightweight since it won’t have lots of complex UI anyway. Luckily I made use of CSS variables. CSS Variables allow you to add global variables to the root of your document. This makes it super easy to implement a simple switch like this.

CSS

So on the top of my css file I had this portion which basically sets the global variables:

:root {
  --background: #f0eddd;
  --text: #0d1a1c;
  --title: #0c4f8f;
  --separator: #cab99f;
  --grid-item: #ffffff;
  --shadow: rgba(0, 0, 0, 0.1);
  --light-blue: #64a8ca;
  --blue: #0c7de5;
}

/* Usage */
body {
  background-color: var(--background);
}

Now all I have to do is change these HEX values to a different darker palette. I don’t have to change any other CSS, just the global variables.

To be honest, this took most of my time as I didn’t how to choose colors that would make the website:

  • has a dark theme
  • looks like the original light version
  • its colors are contrasting enough to be Accessible

I read this article from Material.io that made it much easier to decide on colors.

/* Dark mode CSS */
[dark] {
  --background: #2a2a2a;
  --text: #ebeeef;
  --title: #ffffff;
  --separator: #0c0d0e;
  --grid-item: #484b65;
  --shadow: rgba(0, 0, 0, 0.35);
  --blue: #459ff3;
  --light-blue: #9bc7f7;
}

I chose to indicate the switch using a [dark] attribute that I will add to the body tag. This selector will overwrite the root variables when enabled.

HTML

Now if I want to new CSS, I can do so by manually adding the dark attribute to the <body>:

<body dark>
...
</body>
Hatem Hassan Portfolio
Looks great!

Now we need to add a UI control that toggles this attribute. What could be better than the crescent emoji 🌒 ? It’s half dark and half light, it will always appear on whatever background. And thanks to the wide emoji support it can be added to the HTML right away. It’s perfect! I don’t need to care about the control state or change its colors based on the current rendered theme.

You can also create a more unique design that matches your website, I didn’t want to spend more than 10 minutes on this experiment 😃

<div id="theme-switcher" onclick="switchTheme()"> 🌒 </div>

I added some CSS to position it as a Material Design style floating button in the bottom right corner.

#theme-switcher {
  position: fixed;
  bottom: 0.5em;
  right: 0.5em;
  cursor: pointer;
  font-size: 30px;
}

JavaScript

If you noticed that onclick event handler in the earlier HTML block, you are correct! We are going to use this to toggle the dark attribute.

function switchTheme() {
  document.querySelector('body').toggleAttribute("dark");
}
// I would say it's a better practice to store the body element in a variable outside the function.

Voila! I have a dark theme in my website 🎉

Going the extra mile

Two more things I can add to this to make it more usable:

For the first one, I need to store the state of the dark attribute in the browser localStorage, and for the second one we can check for device preferences using window.matchMedia().

Then I should listen to page onload event to do these checks and apply the correct theme. The final JavaScript should look like this:

window.onload = function () {
  const darkPref = localStorage.getItem("dark");
  const darkDevice = window.matchMedia("(prefers-color-scheme: dark)").matches;

  // Favor user choice on device settings
  const setAttr = darkPref == "true" || (darkPref == null && darkDevice)

  body.toggleAttribute("dark", setAttr);
  localStorage.setItem("dark", setAttr);
}

function switchTheme() {
  localStorage.setItem("dark", body.toggleAttribute("dark"));
}

💃🏼 Magnífico! 💃🏼