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 has 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 then 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 existance 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 abitrary and can be whatever you want) to classes which 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 of 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;
}
}
In your HTML, you would use the CSS variables instead of the hardcoded hex values.
<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, then you will need to output the class in your HTML tree to enable dark mode. The method in which 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, this means 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
This requires some JavaScript in the browser (outside the scope of this guide) to allow the user to choose their preference (light
or dark
mode). Once selected, you store their preference in a Cookie
that can be 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
A set of HTTP headers that you can request from the browser to get information about 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 users 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 users preference. As you generate the server-side markup, based on this header, you can determine whether to output the dark
class.
Please check out the this web.dev article for more information.
Static-site Generated
A statically generated site creates its markup at build time. The HTML markup is not personalized for the user and therefore not able to adapt to a users 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>
This script would be added in your websites <head>
tag to ensure 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 the <html>
element in your project.