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
Jump to 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.
.dark {
background-color: black; /* from white to black */
color: white; /* from black to white */
}
Then write your 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,
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)!!!
Define theme colors as CSS variables.
:root {
--text-color: black;
--bg-color: white;
}
.dark {
--text-color: white;
--bg-color: black;
}
Then use variables to set colors
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. 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.
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.
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
Use invert filter to enable dark mode.
.dark {
filter: invert(100%);
}
Then add .dark
class to the html
tag. That's the perfect place to it.
<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.
html,
body {
color: #222;
background-color: #fff;
height: 100%;
}
invert(100%)
is same as mix-blend-mode
. But more simpler.
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
.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.
The following example explains how filter: invert(100%) hue-rotate(180deg);
works for link color.
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)
.
.dark,
.dark img {
filter: invert(100%) hue-rotate(180deg);
}
Do the same thing for other media elements.
.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.
.nofilter {
filter: none !important;
}
Then use nofilter
in HTML
<img class="nofilter" src="path/to/image" alt="something"/>
It looks smooth with some transition
.
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.
@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.
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
<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.
button {
background: transparent;
border: transparent;
border-radius: 50%;
cursor: pointer;
}
there is a JavaScript function to toggle dark mode.
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.
.dark, .dark img { filter: invert(100%) hue-rotate(180deg); }
final demo
This is an advanced demo.
I would like to know your thoughts. Maybe I can create a darkmode library.
Enjoy these articles.
Follow me for more articles.
Thanks 💖💖💖
Top comments (2)
Love that solution. Thanks for sharing, man.
Welcome 🤗.