Dark Mode
How to use Atomizer to enable dark mode on your site.
Dark mode has become a standard feature across many websites. OS level support and the prefers-color-scheme
CSS media feature have made it even easier to enable dark mode.
Atomizer does not provide out-of-the-box support for dark mode because it is not required. You can combine the prefers-color-scheme
CSS preference and the existing context
class support to add dark mode to your website.
Continue reading to learn how to use each concept.
Browser Basics
All modern browsers support the prefers-color-scheme
CSS media feature. The feature works by defining or overwriting an existing CSS property via the @media (prefers-color-scheme: dark) {}
directive.
In the example below, we set an initial style for a div
with the .intro
class. We overwrite the style when the OS or user-agent preference is set to dark
.
<div class="intro">Hello World!</div>
/* initial style */
.intro {
background: #eee;
color: black;
}
/* overwrite colors for dark mode */
@media (prefers-color-scheme: dark) {
.intro {
background: #333;
color: white;
}
}
With Atomizer
In Atomizer, you can leverage the context
class syntax to apply styles based on the existence of a class earlier in the HTML tree.
In the example below, the default styles for the div
would be background-color: #eee;
and color: #000;
.
<div class="Bgc(#eee) C(#000)">Hello World!</div>
You can prepend a class, dark
(the name is arbitrary and can be whatever you want) to styles you want to override in dark mode.
- <div class="Bgc(#eee) C(#000)">Hello World!</div>
+ <div class="Bgc(#eee) C(#000) dark_Bgc(#333) dark_C(#fff)">Hello World!</div>
When the dark
class is added earlier in the HTML tree, the styles will change to background-color: #333;
and color: #fff
.
<body class="dark">
<div class="Bgc(#eee) C(#000) dark_Bgc(#333) dark_C(#fff)">Hello World!</div>
</body>
By default, nothing will happen, even if the user has the dark mode scheme enabled. You would need to add the dark
class to your website to "enable" the dark mode classes. Read the Enabling Dark Mode section for tips on how to add the class to your project.
Optimizing with CSS Variables
You could further optimize the approaches mentioned above by combining with CSS custom properties (a.k.a CSS variables) to manage the color changes via the prefers-color-scheme
media feature. Building off the examples above, you would move the color definitions to an external style sheet managed outside Atomizer.
The example below defines the default colors in the :root
block, and their dark
mode overrides via the prefers-color-scheme
block.
/* define the default colors */
:root {
--background-color: #eee;
--text-color: #000;
}
/* overrides for dark mode */
@media (prefers-color-scheme: dark) {
:root {
--background-color: #333;
--text-color: #fff;
}
}
You would use the CSS variables instead of the hardcoded hex values in your HTML.
<div class="Bgc(--background-color) C(--text-color)">Hello World!</div>
The benefit of this approach is that you use the built-in prefers-color-scheme
browser feature and reduce the number of classes you need to manage in the markup.
Enabling Dark Mode
If the CSS variable approach is not possible for your website, you will need to output the class in your HTML tree to enable dark mode. How you add the class will depend on your website architecture.
Server-side Rendered
A server-side rendered website creates the HTML markup on the server. Based on the Request
context of the user, the HTML markup can be personalized. For dark mode support, you could add the dark
class to your HTML based on some preference from the user.
There are a few different ways to do this; we briefly describe them below.
HTTP Cookie
Client-side JavaScript (outside the scope of this guide) allows the user to choose their preference (light
or dark
mode). Once selected, you store their choice in a Cookie
sent on subsequent requests to your site.
Your server-side code would then read the color-scheme preference from the cookie and output the dark
class based on this preference.
Client Hints
You can request a set of HTTP headers from the browser to get information about the device and user preferences. The Sec-CH-Prefers-Color-Scheme
header is relatively new (as of this writing), and modern browser support is ongoing. However, for those browsers that support it, you can get access to the user’s color-scheme preference via this HTTP header on the server.
Your server-side code would initially request this header from the browser, which would return the user’s preference. As you generate the server-side markup, you can determine whether to output the dark
class based on this header.
Please check out the this web.dev article for more information.
Static-site Generated
A statically generated site creates its markup at build time. Unpersonalized HTML markup is delivered and will not be adapted to a user’s preference.
This Atomizer website is statically generated and leverages the CSS variables approach to toggle between light and dark mode.
Since you cannot modify the markup on the server, you can leverage JavaScript to add the dark
class to your HTML. The code below uses the Window.matchMedia()
API to check the users preferred color scheme. If dark
is chosen, then the dark
class is added to the <html>
node.
<head>
<!-- website css ... -->
<script>
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark');
}
</script>
</head>
Add the script in your website’s <head>
tag to guarantee the dark
class is added as soon as possible to avoid a flash of light to dark style.
Client-side Rendered
Client-side rendered websites generate their markup in the browser. Similar to the Static-site Generated approach, the Window.matchMedia()
API can be used to add the dark
class.
The JavaScript you write to add the class will depend on your framework of choice and is outside the scope of this document. In general, you will need to add the dark
class to the component or JavaScript file that outputs your project’s <html>
element.