Operating systems and browsers have been learning new accessibility tricks capable of improving our screen-dependant lives. One of the most popular features, dark mode, helps our eyes adapt to low-light environments.
NEW RESEARCH: LEARN HOW DECISION-MAKERS ARE PRIORITIZING DIGITAL INITIATIVES IN 2024.
Switching between dark to light modes is an incredibly useful feature for airplane pilots, commuters, and other road warriors. While the aesthetics are beautiful, the ability to adapt to ambient lighting can also be mission-critical. However, is it enough to replace the color scheme?
Switching Color Schemes
With the advent of CSS custom properties, dark and light profiles became a matter of setting and replacing variables.
A working example of setting a light color scheme for light-first applications could look like the following:
:root { --background: white; --color: #2b2b2b; } body { background-color: var(--background); color: var(--color); } .darko { --background: #2b2b2b; --color: white; }
To override the theme to a dark profile, we’ll need to set a custom CSS rule to a top-level DOM element. It’s just a simple as setting a new class on the body element.
Some operating systems and browsers let users set the default appearance profile. A powerful new media query can help us identify those settings and let us automatically override the color profile.
@media (prefers-color-scheme: dark) { :root { --background: #2b2b2b; --color: white; } }
With a beautifully crafted profile-switching app, we are almost ready to provide a rich, low-contrast user interface. However, media such as images can disrupt the experience.
Dark Mode for Images
A fully featured appearance mode switching should include images as well. One such approach can be using the background-image property.
#screenshot { background-image: url(settings-light.png); } @media (prefers-color-scheme: dark) { #screenshot { background-image: url(settings-dark.png); } }
The background-image property works excellent, but there are shortcomings related to responsiveness and automatic aspect ratio management. Using Img tags cannot be controlled by CSS, but can JavaScript be aware of the native appearance profile?
JavaScript has an interface for working with media queries. We can use it to obtain ad-hoc information or to listen for changes.
Let’s start by using the right image for our predefined lighting mode.
<script> const screenshotEl = document.getElementById('screenshot'); const matchDark = window.matchMedia('(prefers-color-scheme: dark)'); const setScreenshot = isDark => (screenshotEl.src = `settings-${isDark ? 'dark' : 'light'}.png`); setScreenshot(matchDark.matches); </script>
Now that we are showing the right image, we want to let it react to changes in native settings.
matchDark.addListener(e => { setScreenshot(e.matches); });
This listener attaches to the watchMedia
API and resets the screenshot on every change.
Dark Mode Images Without JavaScript
While JavaScript offers infinite flexibility, staying within HTML and CSS will always yield more optimal performance. An ancient proverb clearly states: Thou shalt not witnesseth Javascript instead CSS or HTML.
The tag is great for responsive images. We are dealing with responsiveness in this case as our app responds to the user’s settings. Setting it up is straightforward.
<noscript> <picture> <source srcset="settings-dark.png" media="(prefers-color-scheme: dark)" /> <source srcset="settings-light.png" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" /> <img id="screenshot" src="settings-light.png" /> </picture> </noscript>
Finally, we can produce a fully optimized profile-switching UI that ships with the color scheme we want and chooses the media that fits well with our environment.
Automatic Image Filtering
Having dedicated light and dark image files is sustainable for smaller-scope projects like landing pages. Some web apps may not have the luxury to have two versions of images. In that case, even a simple grayscale filter can help reduce brightness.
@media (prefers-color-scheme: dark) { :root { --image-filter: grayscale(50%); } img:not([src*=".svg"]) { filter: var(--image-filter); } }
Grayscale filtering is a quick fix for controlling the light intensity in dark mode profiles. We only recommend it when it’s difficult to control images in a web application.
Users deserve the luxury of brightness profile selection. It doesn’t take significant effort to provide such feature since all the hard work is already in your users’ browsers.
The entire code is available for you on CodeSandbox. Please share this article if you think it could be helpful to your network.
Need efficient and scalable software solutions? Learn more about our software development expertise.
Grgur Grisogono
Related Posts
-
How To Use Placeholder Images in React Native
I’ve been working with React Native for over a year now. And one thing that…
-
How To Use Placeholder Images in React Native
I’ve been working with React Native for over a year now. And one thing that…