Down the Rabbit Hole of Implementing Dark Mode on My Website
As a full-stack web developer, I‘ve built countless websites over the years. But I recently went back and implemented a feature I‘ve been hearing a lot of buzz about lately: dark mode. It‘s been popping up all over the place, from major tech giants like Apple and Twitter to small blogs and portfolios.
So, what exactly is dark mode and why should you consider adding it to your website? More importantly, how do you actually implement it from a technical perspective? In this deep dive, I‘ll attempt to answer all those questions and share some learnings from my own dark mode journey.
The rise of dark mode ?
If you‘re not familiar, dark mode is an alternate color scheme for user interfaces that uses light-colored text and other UI elements on a dark background. It‘s essentially the inverse of the traditional "light mode" scheme with dark text on a light background that most websites and apps have used for decades.
Here‘s an example from Twitter, one of the major adopters of dark mode:
While dark themes have existed for a long time, especially in code editors and developer tools, the concept of a mainstream dark mode really started to, well, dawn ? in the late 2010s as OLED smartphone displays became more common.
Unlike traditional LCD screens which have a backlight that‘s always on, the pixels on an OLED screen emit their own light and can turn completely off to display true black. This not only allows for really high contrast ratios but also means that dark interfaces can significantly reduce power consumption.
Android and iOS began offering system-wide dark modes around 2019 to take advantage of this battery-saving potential and give users more visual options. Many websites and apps followed suit with their own theme switchers.
The benefits of dark mode
Okay, so dark mode can extend your phone‘s battery life, but what other benefits does it offer? Turns out, quite a few:
-
Reduced eye strain: Dark modes can be easier on the eyes, especially at night or in low-light environments. The reduced glare and brightness is less harsh than an all-white screen.
-
Improved readability: The high contrast of light text on a dark background can actually make content more readable for some people, particularly those with vision impairments. It reduces the eye‘s focusing burden.
-
Better for concentration: Some studies have found that dark modes can reduce distraction and help users focus better on their work. The darker background makes elements like buttons and menus less attention-grabbing.
-
Saved battery on OLED screens: As mentioned before, dark modes can significantly extend battery life on devices with OLED displays. One test found that using an all-black background on an Android phone used 6x less power than an all-white background.
-
It just looks cool: This is completely subjective, but I think dark interfaces have a certain sleek, futuristic appeal. Maybe it‘s all those sci-fi movies with computers sporting gleaming white text on a black screen. ??♂️
Of course, dark mode isn‘t for everyone or every situation. It can make some types of content like images and videos trickier to view. And long-form reading is arguably better with dark-on-light text. But I think it‘s clear that having a dark theme option is very worthwhile in 2023.
Different approaches to implementing dark mode
So you‘ve decided to hop on the dark mode bandwagon. As a developer, what‘s the best way to actually implement a dark theme on your website? There are a few different approaches, each with their own pros and cons:
1. Separate dark stylesheets
The most basic approach is to create a whole separate CSS file containing your dark mode styles. Then, you use JavaScript to conditionally load either the default light or the dark stylesheet based on the user‘s system preferences or a manual toggle.
Here‘s a simple example:
<head>
<link id="css-theme" rel="stylesheet" href="light-mode.css">
<script>
function setTheme() {
const darkMode = window.matchMedia(‘(prefers-color-scheme: dark)‘).matches;
const cssTheme = document.querySelector(‘#css-theme‘);
cssTheme.setAttribute(‘href‘, darkMode ? ‘dark-mode.css‘ : ‘light-mode.css‘);
}
setTheme();
window.matchMedia(‘(prefers-color-scheme: dark)‘).addListener(setTheme);
</script>
</head>
This script uses the prefers-color-scheme
media feature to detect the user‘s system color scheme preference. If they prefer dark mode, it loads the dark CSS file, otherwise it loads the default light one. The addListener
part makes it so the stylesheet toggles automatically whenever the user‘s system preference changes.
The upside of this approach is that it keeps your HTML markup and JS logic clean by handling all the style switching in CSS. The downside is that it involves an extra network request to fetch a whole separate stylesheet.
2. CSS custom properties (variables)
An alternative is to define your light and dark color schemes as a set of CSS custom properties on the :root
element. Then you can reference those variables throughout your CSS to build your themed styles.
Here‘s a basic example:
:root {
--light-bg: #fff;
--light-text: #222;
--dark-bg: #222;
--dark-text: #fff;
}
body {
background: var(--light-bg);
color: var(--light-text);
}
@media (prefers-color-scheme: dark) {
:root {
--light-bg: var(--dark-bg);
--light-text: var(--dark-text);
}
}
The clever bit here is the prefers-color-scheme
media query which reassigns the light variables to use the dark values when a user prefers dark mode. This results in dark mode styles without needing to redefine every CSS property.
The benefit of this approach is you can keep your theming logic more self-contained in CSS rather than sprinkling it throughout your markup and JS. You can also avoid an extra HTTP request for a separate stylesheet. The downside is it requires broad browser support for custom properties and prefers-color-scheme
(which is pretty good but not universal).
3. Inline styles or class-swapping
The third major approach is to add inline style
attributes or toggle CSS classes to apply your light/dark themes directly to elements in the HTML. You‘d use JavaScript to modify the DOM when the page loads or a user toggles the theme.
Here‘s a simple demo of the class-swap method:
<body class="light">
...
<button id="theme-toggle">Toggle Dark Mode</button>
<script>
const toggle = document.querySelector(‘#theme-toggle‘);
toggle.addEventListener(‘click‘, function() {
document.body.classList.toggle(‘light‘);
document.body.classList.toggle(‘dark‘);
});
</script>
</body>
body.light {
background: white;
color: black;
}
body.dark {
background: black;
color: white;
}
This code uses a button to toggle .light
and .dark
classes on the <body>
element which have corresponding light and dark mode styles defined in CSS.
The benefit of these approaches is they‘re simple to understand and implement. They also don‘t depend on cutting-edge CSS features. The inline style
method gives you ultimate control and flexibility in customizing your themes.
The downside is they can dirty up your markup with lots of repetitive style
attributes or class names. And they put the burden on JavaScript to manage all the theme switching logic instead of leveraging the browser‘s built-in styling engine.
So which method should you use?
Ultimately, there‘s no one "right" way to implement dark mode. It depends on your project‘s requirements and constraints.
If you‘re building a simple website or web app, the CSS custom properties method (#2) is a good balance of power and maintainability. It keeps your styles separate from your HTML/JS and avoids excess network requests.
If you need total control over your themes or have to support older browsers, the class-swapping method (#3) is a good choice. It‘s easy to reason about and lets you fine-tune your dark mode on an element-by-element basis.
If you‘re building a larger web app with a complex design system, you might opt for CSS-in-JS solution where you define your themed properties as JavaScript values. This allows maximum flexibility and reusability across components.
The method I chose
For my personal website, which is a relatively straightforward portfolio/blog type site, I decided to go with the CSS custom properties approach. I liked how I could keep my light and dark themes defined separately from my components and pages.
Here‘s a snippet of the key CSS:
:root {
--light-text: #222;
--light-bg: #fff;
--light-link: #0033cc;
--light-code: #f7f7f7;
--dark-text: #ddd;
--dark-bg: #15202b;
--dark-link: #8ab4f8;
--dark-code: #242424;
}
body {
color: var(--light-text);
background: var(--light-bg);
}
a {
color: var(--light-link);
}
pre, code {
background: var(--light-code);
}
@media (prefers-color-scheme: dark) {
:root {
--light-text: var(--dark-text);
--light-bg: var(--dark-bg);
--light-link: var(--dark-link);
--light-code: var(--dark-code);
}
}
And the JavaScript I used to implement a manual toggle button:
const toggle = document.querySelector(‘#theme-toggle‘);
toggle.addEventListener(‘click‘, function() {
const isCurrentlyDarkMode = document.documentElement.classList.contains(‘dark‘);
if (isCurrentlyDarkMode) {
document.documentElement.classList.remove(‘dark‘);
localStorage.setItem(‘theme‘, ‘light‘);
} else {
document.documentElement.classList.add(‘dark‘);
localStorage.setItem(‘theme‘, ‘dark‘);
}
});
const savedTheme = localStorage.getItem(‘theme‘);
if (savedTheme === ‘dark‘) {
document.documentElement.classList.add(‘dark‘);
} else if (savedTheme === ‘light‘) {
document.documentElement.classList.remove(‘dark‘);
}
This stores the user‘s theme preference in localStorage
and toggles a .dark
class on the root <html>
element to override the default light theme. I like how this seamlessly combines the CSS variable and class-swapping approaches.
Here‘s the final result:
I‘m pretty happy with how it turned out! The dark theme feels clean and modern without being overly heavy. And it was surprisingly easy to implement with this method.
Dark mode design tips
While I was working on the dark theme for my site, I learned a few key things about designing dark interfaces:
-
Use a dark gray, not true black. For the background, a true black (
#000
) can look a bit harsh and unnatural. A very dark gray is easier on the eyes. I settled on#15202b
which is the same shade Twitter uses. -
Don‘t make text pure white. Similarly, a bright white text can cause eye strain with the high contrast. An off-white (like
#ddd
or#ccc
) is a better choice for body copy. -
Desaturate colors in dark mode. Bright, saturated colors that work on a light background can be overwhelming on a dark one. For things like links, buttons, and accents I dialed down the saturation to compensate.
-
Reduce use of shadows. Drop shadows and glows look odd against a dark backdrop since they‘re meant to simulate light. I removed most of my shadows and replaced them with extra border or outline styles.
-
Don‘t forget about accessibility! It‘s critical that your dark theme meets the same accessibility standards as your light theme (a 4.5:1 contrast ratio for normal text, 3:1 for large text). I used tools like WebAIM‘s Contrast Checker to verify this.
Here‘s an infographic showing the main color contrast ratios to aim for:
Following these principles helped me create a dark mode design that was not only visually appealing but also usable and accessible. When in doubt, always prioritize readability and conserve color use!
Research & data on dark mode
Since adding dark mode to my site got me curious, I decided to dig into some of the research and data behind dark themes. Here are a few interesting stats I came across:
-
According to a 2020 study by the Nielsen Norman Group, around 60% of users in their 20s use dark mode at least sometimes, compared to only 20% of users 60 and older.
-
Android Authority found that over 81% of their readers use dark mode on their phones, and 93% of them keep it on all the time!
-
A 2021 paper from Purdue University analyzed power measurements and found that dark mode on average reduces battery consumption by 3-9% for several popular mobile apps.
-
One small study discovered that dark mode could help users fall asleep an average of 8 minutes faster compared to light mode when using devices at night.
-
Another experiment concluded that light mode leads to better performance on visual memory tasks, while dark mode may be better for reading comprehension tasks.
So while the jury‘s still out on whether dark mode is conclusively "better" than light mode, it‘s clear that a significant portion of users prefer it and there are some compelling potential benefits. At the very least, it doesn‘t hurt to provide it as an option!
Conclusion & takeaways
Adding dark mode support to my website was an insightful little project. What I assumed would be a quick UI tweak ended up teaching me a lot about color theory, contrast ratios, and user preferences.
My key takeaways from implementing dark mode were:
- The CSS custom properties method is a clean and powerful way to define separate light/dark themes
- It‘s important to design a dark theme from the ground up, not just invert colors
- Prioritize readability and accessibility over purely aesthetic choices
- A significant chunk of users (especially younger ones) prefer dark themes
- Automatic OS-level color scheme switching is a neat feature to support
Overall, I‘m glad I took the time to implement a dark mode on my site to give users more choice and flexibility. And now that I have this CSS variable foundation in place, it‘d be fairly trivial to add additional alternate color schemes down the line.
If you‘re considering adding a dark theme to your own website, I highly recommend it! Feel free to use the code snippets and design tips from this article as a starting point.
Happy coding, and may your dark modes be readable and stylish! ?️?✨