I have a simple message for web developers, start adding the color-scheme
property to your webpages.
<!--
The page supports both dark and light color schemes,
and the page author prefers dark.
-->
<meta name="color-scheme" content="dark light">
or you can even add it using css
/*
The page supports both dark and light color schemes,
and the page author prefers dark.
*/
:root {
color-scheme: dark light;
}
I absolutely detest sites that "have a dark mode, BUT DON'T MAKE THE SCROLLBAR DARK!", a great example of this is docusaurus.
Docusarus Why???
I actually tweeted at them asking why the scrollbar isn't dark as well, 🤣
The light mode scrollbar hurts the eyes, and ruins the look of the site, so, for the sake of everyone who has eyes and likes dark mode, please use color-scheme
, you can even use it together with your dark mode toggle by using css, for example, one of the sites I made for a client josephojo.com
When using the color-scheme
property you can turn form elements, webpage background, text color, and scrollbars dark, a more famous example would be, github,
Notice, how the scrollbar is dark, and doesn't burn the eyes, they're able to do it, by using the meta tag.
For josephojo.com I used the color-scheme
css property together with @media (prefers-color-scheme: dark) {}
and the .dark
class, the final result is
html {
color-scheme: light;
}
html.dark {
color-scheme: dark;
}
@media (prefers-color-scheme: dark) {
html:not([data-theme]) {
color-scheme: dark;
}
}
When creating the site I used tailwindcss, with the dark mode set to "class", my tailwind config looked like this,
module.exports = {
darkMode: 'class',
// ...
}
For those who haven't used tailwindcss
before, it's basically the same as defining a class that when added to the html element will signal that the site is in dark mode.
Or in simpler terms it's,
<html class="dark">
<!-- ... -->
</html>
You: Wait, but, how did you handle the theme toggle button?
Me: I'm glad you asked.
Now that we have some boilerplate code, all you really need to do, is setup a toggle that will remember our current theme state.
While developing josephojo.com, I found that you have to set your theming system to support the native media theme before anything else, it's generally less painful to the user, that's why I set html:not([data-theme])
in the prefers-color-scheme: dark
media query,
/* ... */
@media (prefers-color-scheme: dark) {
html:not([data-theme]) {
color-scheme: dark;
}
}
/* ... */
html.dark
represents the dark theme applied by tailwind
and [data-theme]
represents the currently applied theme, if data-theme
is different from the local storage, then the theme was manually toggled and the page should use the new theme in data-theme
as well as update the local storage theme, otherwise, it should use the local storage theme as data-theme
, but because data-theme
is only applied to the html
element after javascript is loaded we can tell our css to use the default dark theme if prefers-color-scheme: dark
and the html element doesn't have the data-theme
attribute.
The result you get is this,
As you saw at the end there, changing the actual browser theme won't permanently change the theme set in local storage, with the idea being, if a user a manually changes the theme they must want to use that theme permanently, otherwise use the system theme.
Here is the code for the theme toggle,
// Based on [joshwcomeau.com/gatsby/dark-mode/]
let getSavedTheme = () => {
const theme = window.localStorage.getItem("theme");
// If the user has explicitly chosen light or dark,
// let's use it. Otherwise, this value will be null.
if (typeof theme === "string") return theme;
// If they are using a browser/OS that doesn't support
// color themes, let's not do anything.
return null;
};
let saveTheme = (theme) => {
// If the user has explicitly chosen light or dark, store the default theme
if (typeof theme === "string")
window.localStorage.setItem("theme", theme);
};
let mediaTheme = () => {
// If they haven't been explicitly set, let's check the media query
const mql = matchMedia("(prefers-color-scheme: dark)");
const hasMediaQueryPreference = typeof mql.matches === "boolean";
if (hasMediaQueryPreference) return mql.matches ? "dark" : "light";
};
const html = document.querySelector("html");
// Get theme from html tag, if it has a theme or get it from localStorage
let checkCurrentTheme = () => {
let themeAttr = html.getAttribute("data-theme");
if (themeAttr) return themeAttr;
return getSavedTheme();
};
// Set theme in localStorage, as well as in the html tag
let applyTheme = (theme) => {
html.className = theme;
html.setAttribute("data-theme", theme);
};
try {
// if there is a saved theme in local storage use that,
// otherwise use `prefer-color-scheme` to set the theme
let theme = getSavedTheme();
if (theme == null) theme = mediaTheme();
// set the initial theme
html.setAttribute("data-theme", theme);
html.classList.add(theme);
// If a user changes the system/browser/OS theme, update the site theme as well,
// but don't save the change in local storage
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", (e) => {
applyTheme(e.matches ? "dark" : "light");
});
// On theme toggle button click, toggle the page theme between dark and light mode,
// then save the theme in local storage
document
.querySelector("#theme-toggle")
.addEventListener("click", () => {
let theme = checkCurrentTheme() === "dark" ? "light" : "dark";
applyTheme(theme);
saveTheme(theme);
});
} catch (e) {
console.warn("Theming isn't available on this browser.", e);
}
You can view the demo below, but you need to open the demo in a new tab for it to work properly ( note , I don't mean open the entire Code Sandbox in a new tab, I specifically mean the demo. CodeSandbox has disabled local storage, when the demo is attached to rest of the CodeSandbox instance, so, you need to detach the demo from the CodeSandbox instance).
Also, notice, how I never set the text color, background color, scrollbar color or button styles, that's part of the magic of setting color-scheme
.
You can read more about color-scheme
on web.dev
Please tell me what you think about color-scheme
in the comments below.
Update: Another cool part feature of the color-scheme
meta tag is that Samsung Internet won't force dark mode on your site if it uses the color-scheme
meta tag, from what I can tell Chrome might implement a similar feature in the future. I tweeted about it
You can learn more about this on the Samsung Developers site,
https://developer.samsung.com/internet/blog/en-us/2020/12/15/dark-mode-in-samsung-internet
Photo by Alexander Andrews on Unsplash
Top comments (4)
Unfortunately this is currently not supported in firefox
Unfortunately you are correct, Firefox is the only browser that doesn't support it, but support might be added in the future bugzilla.mozilla.org/show_bug.cgi?....
caniuse.com/?search=color-scheme
Also, From what I can tell, Firefox can automatically change the color of the scrollbar based on the background you set, so, as long as you test your dark mode on Firefox before publishing your site, you should be good.
I'd rather handcode it than leave it to the browsers. It's weird how fellas add a dark mode to their site and ignore the scroll bar. Nice article man 🍻
Thanks