Thanks to the CSS Media Queries Level 5 spec, now there’s a really easy and awesome way to match your website’s color scheme with what a user has chosen in their operating system to be their appearance of choice.
Thanks to a new media query called prefers-color-scheme
now you can adapt and honor if I want a dark
or light
display of content. It’s really easy, and works super well when combined with CSS custom properties, aka CSS variables
by the non-W3C peeps.
Show me the code, dude!
Here is the Media query to target dark
themes.
@media (prefers-color-scheme: dark) {
/* take me to the dark side dude! */
/* define your dark theme here */
}
That’s all!
You can also target the light
color scheme
@media (prefers-color-scheme: light) {
/* Whoa! Super bright around here */
/* define your light theme here */
}
Combined with CSS variables
What’s really neat is, when you mix css variables with the media query, like I’m doing on my blog now.
Oh, and you don’t need to do both media queries, really.
Just set your colors up in the base :root
block, and then override them
in the dark
section. Here’ an example of what I do on here.
:root {
--color-fg: #1c0e00;
--color-body-bg: #f1f1f1;
}
@media (prefers-color-scheme: dark) {
:root {
--color-fg: #fefff5;
--color-body-bg: #0a0e13;
}
}
Boom! You can go to your display settings, toggle between light, dark and see your theme
do its magic.
Note: There is nothing stopping you from doing other things beside just changing colors around based on a theme.
CSS in JS color schemes
I know some fancy people, me included, like to mess with CSS-in-JS. So, you might be asking, how can I use this in my Styled Components, or Emotion dude?
It’s super simple yet again. You can create a couple of static variables to target dark and light and use them in your CSSinJS components.
Here’s an example using EmotionJS, but it works pretty much the same with Styled Components.
// create a couple of constants
// just to make them easier to re-use
const DARK = "@media (prefers-color-scheme: dark)";
const LIGHT = "@media (prefers-color-scheme: light)";
// use them to target stuff
const StyledComponent = styled("div")({
color: "var(--color-for-light)",
[DARK]: {
color: "var(--color-for-dark)",
},
});
JavaScript detection
So, if you wanted to you could even sniff the user’s color scheme setting straight from JavaScript, which could be useful in your React apps for example.
const LIGHT = window.matchMedia("(prefers-color-scheme: light)").matches;
const DARK = window.matchMedia("(prefers-color-scheme: dark)").matches;
The matchMedia
method returns a MediaQueryList
object, who’s .matches
property is a boolean flag. From there you should be able
to detect what the user “wants”. It’s up to you to decide if you want to honor their
preferences, or do your own thing.
Fingerprinting
There is a growing concern about data being mined by browser fingerprinting algorithms. While I was jokingly referring to the dark side 🦹🏻♀️ before when referring to a user preference, this type of exploitation is actually quite serious and concerning.
App level theme preferences
If you wanted to write your own logic for managing user preferences, you could do that as well. Personally I don’t think this makes sense for most web apps. But there are use cases, sure. For a full write up on that head over to CSS Tricks where Maks Akymenko has already written a great post on how to make a custom theme toggler using React hooks and ThemeProvider.
The future
As of right now the spec defines only 2 values for the prefers-color-scheme
selector: light
and dark
. In the
future there may be new options added, think sepia
or grayscale
etc. While not supported yet by any browsers,
keep an eye out for prefers-contrast
as well.
Browser support
prefers-color-scheme
is already supported in all modern browsers. Have fun making themes!