This content originally appeared on Bram.us and was authored by Bramus!
Mockup of a browser in Dark Mode + a Light Mode override button (🌕) which the website on display takes into account
When implementing Dark Mode and ways to toggle it, developers currently need to duplicate code and roll their own toggle implementation.
What if, instead, browsers would take care of all that?
~
Table of Contents
- Dark Mode 101
- The problem with Dark Mode Toggles
- Code Duplication as a red flag
- A better way of toggling
- Inherited Settings
- Demo
- In Summary
- Spread the word
~
# Dark Mode 101
Thanks to Media Queries Level 5 we can implement Dark Mode using only CSS. By also using CSS Custom Properties in the mix, it becomes very manageable:
html {
color-scheme: light dark; /* This site supports both light and dark mode */
}
:root { /* Default Light Mode colors */
--primary-color: black;
--primary-background: white;
}
@media(prefers-color-scheme: dark) {
:root { /* Dark Mode colors */
--primary-color: white;
--primary-background: black;
}
}
body {
color: var(--primary-color);
background: var(--primary-background);
}
Depending on the System/OS Color Theme setting, the site will either use black text on a white background (Light Mode) or white text on a black background (Dark Mode).
~
# The problem with Dark Mode Toggles
To offer users an easy way to switch between Light and Dark Mode, a lot of developers are offering toggle buttons their website. Instead of having the user go to the System Preferences, they can switch between Light/Dark Mode without ever leaving the site.
Developers who ever did implement a Light/Dark Mode toggle, might have noticed these side-effects/quirks:
-
To make this work, developers need to rely on either the Checkbox Hack or JavaScript to alter something – mostly a class or attribute – on the document:
<select name="color-scheme-"> <option value="system">System</option> <option value="light">Forced Light</option> <option value="dark">Forced Dark</option> </select>
document.querySelector('color-scheme').addEventListener('change', (e) => { document.documentElement.setAttribute('data-force-color-mode', e.target.value); });
-
Because of the way it is implemented (see step 1), duplication of the CSS code is required:
:root, :root[data-force-color-mode="light"] { /* Default Light Mode colors + Forced Light Mode */ --primary-color: black; --primary-background: white; } /* Dark Color Scheme (System Preference) */ @media (prefers-color-scheme: dark) { :root { --primary-color: white; --primary-background: black; } } /* Dark Color Scheme (Override) */ :root[data-force-color-mode="dark"] { --primary-color: white; --primary-background: black; }
-
To persist the setting across pages and reloads, JavaScript is required:
- Write the selected value after changing it (Most likely a cookie or Local Storage)
- Read the value on page load, to make sure the proper override class is set on the document
-
You'll need some more JavaScript as well to have the page respond to changes of the System Preference.
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); mediaQuery.addListener(() => { // Make sure the dropdown is up-to-date based on mediaQuery.matches });
But then again: do you still respond if the override has been set?
Above that many implementations I have seen don't take the "System" value into account. By omitting this option, the sites will never be able to respond to the system preference again, as they always have an override applied.
~
# Code Duplication as a red flag
One the things that immediately stands out as a red flag is the Code Duplication part. It's been bothering me before, and it is still bothering me today. The other listed issues are also issues, but are somewhat acceptable.
Thinking of ways to solve the Code Duplication part, I see these ideas floating around:
-
If we had a
:media()
pseudo-class available (see this proposal by Lea Verou), they could query it inside:is()
:root:is(.dark, :media(prefers-color-scheme: dark)) { --primary-color: white; --primary-background: black; }
-
In similar fashion, if Media Queries could also be used as selectors (see this suggestion), you can also group the selectors together
@media (prefers-color-scheme: dark) p, .dark-mode p { --primary-color: white; --primary-background: black; }
(Sidenote: I’m not a fan of this suggestion to be honest)
-
If we were able to programmatically change the value for
prefers-color-scheme
, the duplicated CSS part could be omitted. Something like:document.querySelector('color-scheme').addEventListener('change', (e) => { window.setMedia('prefers-color-scheme', e.target.value); });
UPDATE: Also see this CSSWG issue
These solutions, however, do not tackle any of the other listed issues. What about the JS requirement to respond to toggle changes, persist the value, read the value on load, …?
~
# A better way of toggling
What if, instead of having developers try and implement their own toggle and all its quirks, the browser would offer this functionality to the user?
Think of a button that is part of the browser's UI that you could click. Something like the button Chrome DevTools already sports, but then part of the browser UI, available for all users
With such a button in place, the user can easily toggle the preference, and developers don't need to do anything to offer easy switching.
Above that it would allow users to have their OS set to Dark Mode, while reading websites in Light Mode (or vice versa)
~
# Inherited Settings
The way I see it, the chosen Dark/Light value would be persisted on a per-site basis – i.e. altering it would store the chosen preference for only that site.
To not force users to set the value for every new site they visit, there would also be a setting at the browser level.
Combined, you'd get a value that inherits down from the system level down to the site level:
- Dark/Light Setting at the OS level
- Dark/Light Setting at the Browser level (default value: inherit from OS)
- Dark/Light Setting at the Site level (default value: inherit from Browser)
With these in place, users get the freedom to have the sites they visit use a different color scheme from their OS (if they want) and to also make exceptions for certain websites.
UPDATE: Also see this Chromium issue
~
# Demo
You can play around with the CodePen below to see how altering these settings at the various levels would propagate onto a dummy browser UI + two webpages:
See the Pen Browser Level Dark Mode Toggle by Bramus (@bramus) on CodePen.
Be sure to try out the values to OS:Light+Browser:Dark+Site:Light for bram.us
, and then switch tabs to other.site
:
- The site
bram.us
will be Light, due to the Site setting being Light - The site
other.site
will be Dark, due to the Browser setting being Dark and its specific Site setting being inherit
đź‘Ź Shout out to Firefox for shipping a Dark/Light Setting at the Browser level in their Firefox 100 release. They also have an extra option, where the chosen browser theme can decide whether to apply Light or Dark Mode.
Now let's hope we can also get a setting at the Site Level?
~
# In Summary
In summary, I think there should be easier ways to switch Dark Mode on a per-site basis. Ideally, switching/overriding should be a simple button (or the like) at the browser level. That way we can get rid of all those custom light/dark mode buttons, and the browsers can sync the preference values to all the devices a user uses.
Something that could also make the developers lives easier – but would still leave them custom buttons in place – is a way to programmatically change the value from within JavaScript
window.setMedia('prefers-color-scheme', 'dark');
Or perhaps maybe even both? A scenario where calls to window.setMedia()
would bubble up to the browser (so they can keep their UI in sync? it would be an entirely optional thing to implement for devs, as the browser already provides the button.
Let me know what you think in the comments, or hit me up on Twitter.
~
# Spread the word
To help spread the contents of this post, feel free to retweet the announcement tweet:
When implementing Dark Mode and ways to toggle it, developers currently need to duplicate code and roll their own toggle implementation.
— Bram.us (@bramusblog) May 25, 2022
What if, instead, browsers would take care of all that toggle?
đź”— https://t.co/VIazw2a5gB
🏷 #CSS #JavaScript #DarkMode pic.twitter.com/aEKv6mV6Qj
~
🔥 Like what you see? Want to stay in the loop? Here's how:
This content originally appeared on Bram.us and was authored by Bramus!
Bramus! | Sciencx (2022-05-25T13:22:58+00:00) Dark Mode Toggles Should be a Browser Feature. Retrieved from https://www.scien.cx/2022/05/25/dark-mode-toggles-should-be-a-browser-feature/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.