Adding Dark Mode with CSS & JavaScript
Today, it's pretty much expected for a website to also offer dark mode - i.e. a darker presentation of the page. What might sound like a big requirement is actually easy to implement!
Switching Light & Dark Theme with CSS and Javascript
Ever seen a website that allows you to switch between a light and dark theme and wondered, how you can implement this feature on your website?
If you did, then this tutorial is for you.
There are multiple ways of implementing this feature on your websites or web apps. The easiest way to implement this feature is by using CSS variables and some Javascript.
In this tutorial, you will learn how to implement this feature by making a simple web page that contains a navigation bar and one main section.
At the end of this tutorial, you will have a web page which allows you to change its theme. Here's the final result:
First of all, lets write the HTML needed for this tutorial.
<body class="light-theme"><header><nav><ul><li><label class="switch"><i class="fas fa-adjust"></i><div><input type="checkbox" /><span class="slider round"></span></div></label></li><li>Home</li><li>Services</li><li>Contact</li><li>About</li></ul></nav></header><main>Change My Theme</main></body>
Its simple HTML markup that contains a header
element that contains the navigation bar and the main
element that contains some text. You might have noticed the light-theme
class added on the body
element. It will be be used later to switch between the light and the dark theme. Also note that I have used an icon from fontawesome
library, so make sure to include its CDN in the head
section of the web page.
<head>...<linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/js/regular.min.js"/></head>
Now lets add some CSS to style our web page:
:root {--navHeight: 70px;}* {margin: 0;padding: 0;}.switch {width: 55px;display: flex;align-items: center;justify-content: space-between;}.switch div {position: relative;display: block;background: #eee;width: 30px;border-radius: 50px;padding: 0 5px;box-sizing: border-box;cursor: pointer;}.fa-adjust {transform: rotate(180deg);}.switch input {display: none;}.slider {background-color: #999;transition: 0.4s;border-radius: 34px;height: 12px;width: 12px;display: inline-block;position: relative;}input:checked + .slider {transform: translateX(8px);}nav {background: #d2cca1;box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);font-size: 1.1rem;position: relative;}nav ul {list-style-type: none;}nav ul {display: flex;justify-content: flex-end;align-items: center;flex-wrap: wrap;height: var(--navHeight);padding: 0 10px;}nav ul li {padding: 12px 10px;cursor: pointer;transition: background 0.3s ease;border-radius: 4px;position: relative;}nav ul li:hover {background-color: #dedaba;}main {display: flex;align-items: center;justify-content: center;font-size: 3.5rem;background: #e5c687;height: calc(100vh - var(--navHeight));}
At this point, out web page should like like this:
Now lets add some CSS variables in our CSS that will represent different colors in our webpage.
:root {--navHeight: 70px;--sage: #d2cca1;--paleSpringBud: #dedaba;--goldCrayola: #e5c687;--richBlackForeground: #0f1a20;--white: #f8f8f8;--black: #222;--msuGreen: #2d4739;--gray: #eee;}
Lets add some more CSS variables that will represent colors for different elements in our web page for light and dark theme.
.light-theme {--themeDropDownBg: var(--goldCrayola);--themeIconBorderColor: var(--sage);--navBg: var(--sage);--navLinkHoverBg: var(--paleSpringBud);--mainBg: var(--goldCrayola);--fontColor: var(--black);}.dark-theme {--themeDropDownBg: var(--msuGreen);--themeIconBorderColor: var(--richBlackForeground);--navBg: var(--richBlackForeground);--navLinkHoverBg: var(--msuGreen);--mainBg: var(--msuGreen);--fontColor: var(--white);}
Notice that instead of defining these CSS variables on :root
, we have defined the same set of variables under .light-theme
and .dark-theme
. This is the trick to switch between light and dark theme using CSS variables. As CSS variables cascade downward, we will apply .light-theme
and .dark-theme
to the body
element so that different elements on our web page are colored differently based on the class applied on the body
element.
After adding the CSS variables, let now replace some of the hard-coded colors in our CSS with these CSS variables.
* {color: var(--fontColor);}nav {background: var(--navBg);}nav ul li:hover {background-color: var(--navLinkHoverBg);}main {background: var(--mainBg);}
We are almost done. Now we need to write some Javascript code to add the .light-theme
or .dark-theme
classes on the body
element when the theme toggle switch is clicked.
const themeSwitch = document.querySelector('input');themeSwitch.addEventListener('change', () => {document.body.classList.toggle('dark-theme');});
At this point, we should have a working demo
Final Improvements
Our web page already looks cool but lets do some final improvements to make it look even nicer.
First thing I would like to improve is how our theme switches from light to dark and vice versa. I would like it to transition smoothly from one theme to another instead of instantly jumping from light to dark theme.
Lets add a transition
on the background-color
and color
properties of each element in our web page.
* {transition: background-color 0.6s ease, color 1s ease;}
The result of adding this transition
can be seen below
Second thing I want to improve is the color of the theme toggle switch. Currently, it stays the same no matter which theme is applied. Lets make it so that it changes the color when the theme is changed.
To achieve this, we just need to add a CSS variable that holds the color for the theme switch. We have to add this variable in the .light-theme
and .dark-theme
classes.
.light-theme {--themeSwitchColor: var(--sage);}.dark-theme {--themeSwitchColor: var(--msuGreen);}.slider {background-color: var(--themeSwitchColor);}
Now our switch changes its color according to the current theme
Possible Next Steps
That's it for this tutorial. There are some other improvements that could be made, for example:
Making the navigation bar responsive
One thing you might have noticed here is that currently our theme switches back to light theme when page is refreshed. We can avoid this by saving the current theme in
localStorage
and when the page is refreshed, we read the current theme from thelocalStorage
and set the appropriate theme class on thebody
element
But i will leave the above mentioned improvements for you to implement. I hope you liked this tutorial.