prefers-color-scheme - the dark side

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!

đŸ‘šđŸ»â€đŸŽš