In this post, I want to fix some mistakes I made in other posts, and also include new elements I discovered on this topic since I wrote them.
When this will be released, all other related posts will also be edited to avoid mentioning those errors. I wanted to reference all of them for posterity.
CSS color scheme
In this post, I only mentioned the meta tag with the name color-scheme
:
<meta name="color-scheme" content="light dark" />
But I forgot to mention that this can also be set in the CSS (see MDN):
:root {
color-scheme: light dark;
}
I also wrote:
Note: this will work on both Chrome and Safari, but not Firefox
This has been fixed in Firefox 96 🎉 (see caniuse).
Native system colors
In this post, I only mentioned the use of color-scheme
. But there is also another powerful tool available to us: native system themed colors.
In CSS, you can say that a color should follow the system color for multiple semantic elements, like LinkText
, or Canvas
(background color):
The best is that those colors will have automated variants in dark mode (which is why they should have been mentioned in the “Lazy way” post).
For the whole list of system colors, and their semantic meaning, you can out the MDN page.
:root
with class names
In this post, I wrote:
And as we are using classnames, we cannot use :root as before.
This is wrong 😕. When we read its MDN page, it says:
The
:root
CSS pseudo-class matches the root element of a tree representing the document. In HTML,:root
represents the<html>
element and is identical to the selectorhtml
, except that its specificity is higher.
After some tests, I can confirm that the following works fine:
:root.dark-mode {
/* Works great! */
}
/* Equivalent to html but with a greater specificity */
html.dark-mode {
/* Works great! */
}
:root
has specificity of (0, 1, 0)
, and html
has a specificity of (0, 0, 1)
.
Using data attributes instead of class names
In this post, I mentioned that we were using 2 classes .light
and .dark
. And that we were using this function to control those classes:
const colorScheme = document.querySelector('meta[name="color-scheme"]');
function applyTheme(theme) {
document.body.className = theme;
colorScheme.content = theme;
}
The issue with it, is that it overrides all classes set on the body. This is fine for this post, as we don’t have any other classes, but it may not be in your own application.
A more realistic function would be something like:
const colorScheme = document.querySelector('meta[name="color-scheme"]');
function applyTheme(theme) {
document.body.classList.remove('light');
document.body.classList.remove('dark');
document.body.classList.add(theme);
colorScheme.content = theme;
}
You can see that it's a bit tedious to have to remove all classes, especially if you start to add more themes, like low/high contrast, etc.
A better solution would be to use data attributes, to which we can add the correction we did for the color-scheme
, and for the root :root
:
:root[data-theme="light"] {
color-scheme: light;
--text: black;
--background: white;
}
:root[data-theme="dark"] {
color-scheme: dark;
--text: white;
--background: black;
}
body {
color: var(--text);
background: var(--background);
}
And to set it:
function applyTheme(theme) {
document.documentElement.dataset.theme = theme;
}
(document.documentElement
is the <html>
node, see on MDN)
Real time system mode
In this post, I explained how to use a custom picker and how to use a system
mode.
I forgot to say that this system mode won’t follow the current theme users have on their machine. Instead it just computes this theme when the mode is picked.
This mechanism is more complicated and can be explained in its own post. But in the meantime, the React implementation includes this feature:
Top comments (0)