Scott Hanselman

How to detect if the User's OS prefers dark mode and change your site with CSS and JS

September 14, 2021 Comment on this post [15] Posted in ASP.NET
Sponsored By

Dark Mode SwitchI got a tweet from Stevö John who said he found my existing light theme for my blog to be jarring as he lives in Dark Mode. I had never really thought about it before, but once he said it, it was obvious. Not only should I support dark mode, but I should detect the user's preference and switch seamlessly. I should also support changing modes if the browser or OS changes as well. Stevö was kind enough to send some sample CSS and a few links so I started to explore the topic.

There's a few things here to consider when using prefers-color-scheme and detecting dark mode:

  • Using the existing theme as much as possible.
    • I don't want to have a style.css and a style-dark.css if I can avoid it. Otherwise it'd be a maintenance nightmare.
  • Make it work on all my sites
  • Consider 3rd party widgets
    • I use a syntax highlighter (very very old) for my blog, and I use a podcast HTML5 player from Simplecast for my podcast. I'd hate to dark mode it all and then have a big old LIGHT MODE podcast player scaring people away. As such, I need the context to flow all the way through.
  • Consider the initial state of the page as well as the stage changing.
    • Sure, I could just have the page look good when you load it and if you change modes (dark to light and back) in the middle of viewing my page, it should also change, right? And also consider all the requirements above.

You can set your Chrome/Edge browser to use System Settings, Light, or Dark. Search for Theme in Settings.

Choose your Theme, Dark or Light

All this, and I can only do it on my lunch hour because this blog isn't my actual day job. Let's go!

The prefers-color-scheme CSS Media Query

I love CSS @media queries and have used them for many years to support mobile and tablet devices. Today they are a staple of responsive design. Turns out you can just use a @media query to see if the user prefers dark mode.

@media (prefers-color-scheme: dark) {

Sweet. Anything inside here (the C in CSS stands for Cascading, remember) will override what comes before. Here's a few starter rules I changed. I was just changing stuff in the F12 tools inspector, and then collecting them back into my main CSS page. You can also use variables if you are an organized CSS person with a design system.

These are just a few, but you get the idea. Note the .line-tan example also where I say 'just put it back to it's initial value.' That's often a lot easier than coming up with "the opposite" value, which in this case would have meant generating some PNGs.

@media (prefers-color-scheme: dark) {
body {
color: #b0b0b0;
background-color: #101010;
}

.containerOuter {
background-color: #000;
color: #b0b0b0;
}

.blogBodyContainer {
background-color: #101010;
}

.line-tan {
background: initial;
}

#mainContent {
background-color: #000;
}
...snip...
}

Sweet. This change to my main css works for the http://hanselman.com main site. Let's do the blog now, which includes the 3rd party syntax highlighter. I use the same basic rules from my main site but then also had to (sorry CSS folks) be aggressive and overly !important with this very old syntax highlighter, like this:

@media (prefers-color-scheme: dark) {
.syntaxhighlighter {
background-color: #000 !important
}

.syntaxhighlighter .line.alt1 {
background-color: #000 !important
}

.syntaxhighlighter .line.alt2 {
background-color: #000 !important
}

.syntaxhighlighter .line {
background-color: #000 !important
}
...snip...
}

Your mileage may vary but it all depends on the tools. I wasn't able to get this working without the !important which I'm told is frowned upon. My apologies.

Detecting Dark Mode preferences with JavaScript

The third party control I use for my podcast is a like a lot of controls, it's an iFrame. As such, it takes some parameters as URL querystring parameters.

I generate the iFrame like this:

<iframe id='simpleCastPlayeriFrame' 
title='Hanselminutes Podcast Player'
frameborder='0' height='200px' scrolling='no'
seamless src='https://player.simplecast.com/{sharingId}'
width='100%'></iframe>

If I add "dark=true" to the querystring, I'll get a different player skin. This is just one example, but it's common that 3rd party integrations will either want a queryString or a variable or custom CSS. You'll want to work with your vendors to make sure they not only care about dark mode (thanks Simplecast!) and that they have a way to easily enable it like this.

Dark Mode and Light Mode Simplecast Podcast player

But this introduce some interesting issues. I need to detect the preference with JavaScript and make sure the right player gets loaded.

I'd also like to notice if the theme changes (light to dark or back) and dynamically change my CSS (that part happens automatically by the browser) and this player (that's gotta be done manually, because dark mode was invoked via a URL querystring segment.)

Here's my code. Again, not a JavaScript expert but this felt natural to me. If it's not super idiomatic or it just sucks, email me and I'll do an update. I do check for window.matchMedia to at least not freak out if an older browser shows up.

if (window.matchMedia) {
var match = window.matchMedia('(prefers-color-scheme: dark)')
toggleDarkMode(match.matches);

match.addEventListener('change', e => {
toggleDarkMode(match.matches);
})

function toggleDarkMode(state) {
let simpleCastPlayer = new URL(document.querySelector("#simpleCastPlayeriFrame").src);
simpleCastPlayer.searchParams.set("dark", state);
document.querySelector("#simpleCastPlayeriFrame").src = simpleCastPlayer.href;
}
}

toggleDarkMode is a method so I can use it for the initial state and the 'change' state. It uses the URL object because parsing strings is so 2000-and-late. I set the searchParams rather than .append because I know it's always set. I set it.

As I write this I supposed I could have stored the document.querySelector() like I did the matchMedia, but I just saw it now. Darn. Still, it works! So I #shipit.

I am sure I missed a page or two or a element or three so if you find a white page or a mistake, file it here https://github.com/shanselman/hanselman.com-bugs/issues and I'll take a look when I can.

All in all, a fun lunch hour. Thanks Stevö for the nudge!

Now YOU, Dear Reader can go update YOUR sites for both Light Mode and Dark Mode.


Sponsor:  The No. 1 reason developers choose Couchbase? You can use your existing SQL++ skills to easily query and access JSON. That’s more power and flexibility with less training. Learn more.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
September 21, 2021 4:48
One small step toward saving our eyes. Thanks. Now, I gotta go do it on my site too.
September 21, 2021 8:16
Let's not forget that about 30..50% of the people have astigmatism, and dark mode is basically unbearable for them, as it makes eyes hurt and gives a bad headache.

Also, of course, there is zero evidence that dark mode "saves the eyes", and quite a bit of research has shown the opposite.
September 21, 2021 8:30
Jann,

That's a bit antagonistic. Scott is giving you the option of either dark or light mode, not forcing either upon you.

Also, I have astigmatism, and frankly I prefer dark mode. I have my monitors on my lowest brightness and it still feels painful to see a bright white website at the wrong time of day.

Giving people a choice is not a bad thing.
September 21, 2021 8:49
I am more than a bit bitter about the dark modes, as quite often it becomes the only option. Having the choice is nice, but please do not make make it the default, in neither apps or websites.

I cannot use dark mode at all - about five minutes in, I get a bad headache, which the usual painkillers won't relieve. And not long ago, I had A Very Important Training that used gray-on-black code - I asked this to be changed, but the presenter didn't see the request until few hours in...

September 21, 2021 10:15
Scott is giving you the option of either dark or light mode, not forcing either upon you.


Actually, Scott is forcing the dark mode without given the user a choice. Some of us do prefer to have dark mode on OS but that does not necessarily translate to "everything must be in dark mode".
If it's a user's choice, then it should really be a user's choice: infer the user's preference, true, but still give a way to change the default behaviour.
September 21, 2021 10:22
Wow.

Someone tries to do a nice thing and attracts instant criticism.

Welcome to the internet.
September 21, 2021 11:16
Don't get me wrong Steve, this is a nice thing and I believe that it's a great improvement to many. But, it also shows that user preferences is not a simple (or consensual) topic :)
September 21, 2021 12:23
And you did over a lunch break? Wow, amazing Scott! Thanks for showing us how this works.
September 21, 2021 13:56
Scott, great work, but look at "A.Nogueira"'s reply that uses a blockquote. It needs to be darkened.

Also, there seems to be no way to get a permalink to a comment, which I would have used. Could you make the date an anchor link to its comment ID to make it easy to link to a specific comment like most blogs use?
C B
September 21, 2021 15:31
Hi friends, you're right I'm not forcing dark mode on anyone. The site now respects both OS and Browser settings. You can set OS to dark and browser to light and I'll do the right thing. It's 100% in the control of the user.

CB - Good idea, I'll look into that feature! Can you file a bug? I will also fix the blockquote color.
September 21, 2021 17:51
Hey there - I dislike browsing most sites in Dark Mode, but I prefer having my Windows 10 settings in dark mode. I selectively enable/disable dark mode on various sites to my preferences.

Do you plan to add a feature to toggle the dark mode setting on your sites?
September 21, 2021 18:13
This is just perfect :-)
September 22, 2021 7:07
Thank you for the help. If you need any kind of laptop then you must check out the given site
September 23, 2021 8:34
Thank you!

Dark mode FTW!

I guess I kinda of understand some comments about people preferring dark mode for a lot of things but on some things they prefer the lighter style. I'm sure it would be fairly trivial to add a way to switch it manually with a snippet of JS.
September 23, 2021 14:15
You have provided good information related to CSS and JS. Thanks for the help

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.