Dark mode with 1(or few) line of CSS 🌓
Tags: javascript, webdev, tutorial, css
Published: 2021-11-28 02:28:52

---
title: Dark mode with 1(or few) line of CSS 🌓
published: true
description: Create dark mode with 1 line of CSS with filters, and some tips and tricks.
cover_image: https://source.unsplash.com/vGCErDhrc3E
tags: javascript, webdev, tutorial, css
---

In this week I wanted to add dark mode to my blog(currently in development). So I searched and searched and searched for a simple solution.

I am going to introduce my own idea. But later I found same 2 articles. But their solutions are somewhat incomplete.

**It's CSS filters**

[Jump to final example](#final-example)
[Jump to final demo](#final-demo)

Dark mode has too many names,
- Light-on-dark
- black mode
- dark mode
- dark theme
- night mode

Dark mode is trending from 2018-2019.

There is a silly way to do this.
```css
.dark {
  background-color: black; /* from white to black */
  color: white; /* from black to white */
}
```
Then write your HTML.
```html
...
<body class="dark">
  <h1>Hello World!</h1>
   ...
</body>
...
```
This works finely for examples. In real website there are too many styled items like buttons, alerts, models etc. When you styled all of them for dark mode,
```css
body {
  background-color: ...;
  color: ...;
}
.btn {
  background-color: ...;
  color: ...;
}
.btn.primary {
  background-color: ...;
  color: ...;
}
...
...
.dark {
  background-color: ...;
  color: ...;
}
.dark .btn {
  background-color: ...;
  color: ...;
}
.dark .btn.primary {
  background-color: ...;
  color: ...;
}
...
...
```
Then you doubled your CSS stylesheet.

What is the solution to reduce the size of stylesheet???
Here comes the Superman of CSS. **CSS variables(CSS custom properties)!!!**
![Gif of flying Superman](https://media4.giphy.com/media/10bKPDUM5H7m7u/giphy.gif?cid=ecf05e47w5ceg8oso9q5x83yn2y0k17hsf7tsfwpj49lffx7&rid=giphy.gif&ct=g)
Define theme colors as CSS variables.
```css
:root {
  --text-color: black; 
  --bg-color: white;
}
.dark {
  --text-color: white; 
  --bg-color: black;
}
```
Then use variables to set colors
```css
body, .dark {
  background-color: var(--bg-color);
  color: var(--text-color);
}

.btn {
  background-color: var(--bg-color);
  color: var(--text-color);
}
.btn.primary {
  background-color: var(--bg-color);
  color: var(--text-color);
}
...
...
```
This works finely.

But I am a developer(as a hobby). Not a designer. Which colors should I use in light theme and dark theme?.

Then I found [_Darkmode.js_](https://darkmodejs.learn.uno/). It's using CSS `mix-blend-mode: difference;` to enable dark mode for whole web page. It's complex and have some issues. Instead of using JavaScript anyone can use `mix-blend-mode` in CSS to enable dark theme. The following example explains how `mix-blend-mode: difference;` works. 
```js
light-theme-color = rgb(x, y, z)
dark-theme-color = rgb(255-x, 255-y, 255-z)
```
It converts the color of links from blue to odd yellow color.
```js
light-theme-color = rgb(0, 0, 238) = blue
dark-theme-color = rgb(255-0, 255-0, 255-238) = rgb(255, 255, 17) = yellow
```

After few days, I got an idea... **CSS filters**
![A man got an idea and someone hits him using a glass bottle](https://media1.giphy.com/media/3oz8xP6SaSkSU9dhcI/200.webp?cid=ecf05e47ppmd55tt4wfw2a95r1kml5soawbfrxywsv535gg2&rid=200.webp&ct=g)
Use invert filter to enable dark mode.
```css
.dark {
  filter: invert(100%);
}
```
Then add `.dark` class to the `html` tag. That's the perfect place to it.
```html
<html class="dark">
   ...
</html>
```
This method won't work without text color and background color. And set `height` to `100%` on `html` and `body` tags for a whole page dark theme in contentless pages.
```css
html,
body {
  color: #222;
  background-color: #fff;
  height: 100%;
}
```
`invert(100%)` is same as `mix-blend-mode`. But more simpler.
```js
light-theme-color = rgb(x, y, z)
dark-theme-color = rgb(255-x, 255-y, 255-z)
```

But this method has the same issue. links are yellow in dark mode.
To fix this we should do something like this
```css
.dark {
  filter: invert(100%) hue-rotate(180deg);
}
```

`hue-rotate(180deg)` changes the color on the hue wheel to the opposite color on the hue wheel. Here is hue wheel.
![hue wheel](https://cdn.pixabay.com/photo/2016/10/14/15/21/colour-wheel-1740381_640.jpg)

The following example explains how `filter: invert(100%) hue-rotate(180deg);` works for link color.
```js
light-theme-color = rgb(0, 0, 238) = blue
inverted-color = rgb(255-0, 255-0, 255-238) = rgb(255, 255, 17) = yellow = hsl(60, 100%, 53%)
hue-rotated-color = hsl(270, 100%, 53%) = light-blue
```

This filter is applied to images too. So images looks ugly. To remove that filter on images, use the filter again. So same filter applied to the image twice. `inverse(100%) X inverse(100%) = inverse(0)` and `hue-rotate(180deg) X hue-rotate(180deg) = hue-rotate(0)`.
```css
.dark,
.dark img {
  filter: invert(100%) hue-rotate(180deg);
}
```

Do the same thing for other media elements.
```css
.dark,
.dark img,
.dark picture,
.dark video,
.dark canvas {
  filter: invert(100%) hue-rotate(180deg);
}
```

Create a class named `nofilter` to apply when you need to apply dark theme to media elements.
```css
.nofilter {
  filter: none !important;
}
```
Then use `nofilter` in HTML
```html
<img class="nofilter" src="path/to/image" alt="something"/>
```

It looks smooth with some `transition`.
```css
html,
img,
picture,
video,
canvas {
  transition: filter 0.3s ease-in-out;
}
```

How can I know the preference of user???. There is a CSS media query to do this.
```css
@media (prefers-color-scheme: dark) {
  html,
  img,
  picture,
  video,
  canvas {
    filter: invert(100%) hue-rotate(180deg);
  }
}
```

I want to create a dark mode toggle button and store user preference. + or automatically detect user preference. `localstorage` is perfect to store preference. and use `window.matchMedia` to check if CSS media query is matching to current state.
```js
if (
  localStorage.theme === "dark" ||
  (!("theme" in localStorage) &&
    window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
  document.documentElement.classList.add("dark");
} else {
  document.documentElement.classList.remove("dark");
}
```
This is my toggle button, with a SVG
```html
<button onclick="toggleDark()">
  <svg aria-hidden="true" data-prefix="fas" data-icon="moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 2rem; width: 2rem;" class="svg-inline--fa fa-moon">
    <path fill="currentColor" d="M32 256C32 132.2 132.3 32 255.8 32c11.36 0 29.7 1.668 40.9 3.746 9.616 1.777 11.75 14.63 3.279 19.44C245 86.5 211.2 144.6 211.2 207.8c0 109.7 99.71 193 208.3 172.3 9.561-1.805 16.28 9.324 10.11 16.95C387.9 448.6 324.8 480 255.8 480 132.1 480 32 379.6 32 256z"></path>
  </svg>
</button>
```
Button is rounded and transparent.
```css
button {
  background: transparent;
  border: transparent;
  border-radius: 50%;
  cursor: pointer;
}
```
there is a JavaScript function to toggle dark mode.
```js
let toggleDark = () => {
  let result = document.documentElement.classList.toggle("dark");
  localStorage.theme = result ? "dark" : "light";
};
```

### final example
Then I am going to introduce minimal subset of above code as the tricky one liner. This code is enough for many sites.
```css
.dark, .dark img { filter: invert(100%) hue-rotate(180deg); }
```

### final demo
This is an advanced demo.
{% codepen https://codepen.io/ksenginew/pen/gOxVzXM %}

I would like to know your thoughts. Maybe I can create a darkmode library.

Enjoy these articles.
Follow me for more articles.
Thanks 💖💖💖