Back to overview
Profile image of Jonas Duri
Jonas Duri
Sat, 19 Jan 2019
Discuss on Medium

Use "prefers-color-scheme" to detect macOS dark mode with CSS and Javascript

Screenshot of my website using a dark theme
Dark Mode
Screenshot of my website using a light theme
Light Mode

Since the introduction in MacOS Mojave "Dark Mode" has made its way to the mainstream.

If a user chooses to use "Dark Mode" the overall experience of the OS adapts to provide a consistent user experience.

Many companies introduced a dark theme for their apps in 2018. Spark, *Things 3, Sketch or Ulysses to name a few.

There is a website which has a comprehensive list of many more apps and websites. https://darkmodelist.com/.

In this post, I'm going to explain how and why you should consider making a dark theme for your website too.

What about the web

There are reasons why a person might prefer a dark theme over a light one.

  • It reduces stress on the eyes when reading in a dark environment.
  • Dark mode is enjoyable to look at.
  • It can save battery when using an OLED screen.
  • Better contrast.
  • Less Blue Light which supports a better sleep

Developers should build software that respects a users decision for a certain user experience.

The problem is that there is currently no available cross-browser API to tell if a user has "Dark Mode" enabled. While many websites offer a dark version, it mostly comes down to a "per-website" setting that you have to activate.

Dark version of Youtube
Youtube
Ars Technica website using the dark theme.
Ars Technica

The CSS Working Group already has a draft to bring a native API for user preferences to the browser. As of today, only Safari Technology Preview (Release 73) supports prefers-color-scheme Media Query. It is part of Media Queries Level 5 which you can read more about here.

How to make use of prefers-color-scheme today

Chances are that your users don't use Safari Technology Preview so you can either wait for the implementation to hit all major browsers or find a different solution.

For this website, I choose to respect the user's system preference while providing a sensible default.

If you happen to visit my website during night time, you see the dark version, during the day the light version, however, if you use a supported browser it uses the system preference.

function activateDarkMode() {
  const rootElement = document.querySelector(':root')
  const darkTheme = {
    '--background-color': '#1e1e1e',
    '--primary-color': '#157efb',
    '--font-color': '#dedede',
    '--subtle-primary-color': '#151513',
    '--block-background-color': '#323232',
    '--menu-item-color': '#dedede',
    '--menu-item-hover-color': '#157efb',
    '--menu-item-alert-bg': '#151513',
    '--menu-item-alert-shadow': '#151513',
    '--alert-border-color': '#000',
    '--tertiary-color:': '#727680'
  }
  for(k in darkTheme) {
    rootElement.style.setProperty(k, darkTheme[k])
  }
}

function activateLightMode() {
  const rootElement = document.querySelector(':root')
  const lightTheme = {
    '--background-color': '#fff',
    '--page-width': '70em',
    '--primary-color': '#151513',
    '--font-color': '#151513',
    '--subtle-primary-color': '#151513',
    '--block-background-color': '#f1f3f4',
    '--menu-item-color': '#000',
    '--menu-item-hover-color': '#000',
    '--menu-item-alert-bg': '#ffffff',
    '--menu-item-alert-shadow': '#dfe1e7',
    '--alert-border-color': '#dfe1e7',
    '--tertiary-color:': '#727680'
  }
  for(k in lightTheme) {
    rootElement.style.setProperty(k, lightTheme[k])
  }
}
/**
 * Sets a color scheme for the website.
 * If browser supports "prefers-color-scheme" it will respect the setting for light or dark mode
 * otherwise it will set a dark theme during night time
 */
function setColorScheme() {
  const isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches
  const isLightMode = window.matchMedia("(prefers-color-scheme: light)").matches
  const isNotSpecified = window.matchMedia("(prefers-color-scheme: no-preference)").matches
  const hasNoSupport = !isDarkMode && !isLightMode && !isNotSpecified;

  window.matchMedia("(prefers-color-scheme: dark)").addListener(e => e.matches && activateDarkMode())
  window.matchMedia("(prefers-color-scheme: light)").addListener(e => e.matches && activateLightMode())

  if(isDarkMode) activateDarkMode()
  if(isLightMode) activateLightMode()
  if(isNotSpecified || hasNoSupport) {
    console.log('You specified no preference for a color scheme or your browser does not support it. I Schedule dark mode during night time.')
    now = new Date();
    hour = now.getHours();
    if (hour < 4 || hour >= 16) {
      activateDarkMode();
    }
  }
}

This website uses CSS Custom Properties to make the color scheme easily changeable via javascript. Be aware of the browser support! If you have to support older browsers you can use a polyfill.

Next is the window.matchMedia API. https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia. It supports testing media queries programmatically using JavaScript. There is also a polyfill available for older browsers.

You can also choose a more straightforward approach and use CSS only.

:root {
    --background-color: #fff;
    --page-width: 70em;
    --primary-color: #151513;
    --font-color: #151513;
    --subtle-primary-color: #151513;
    --block-background-color: #f1f3f4;
    --menu-item-color: #000;
    --menu-item-hover-color: #000;
    --menu-item-alert-bg: #ffffff;
    --menu-item-alert-shadow: #dfe1e7;
    --alert-border-color: #dfe1e7;
    --tertiary-color:: #727680;
}

@media (prefers-color-scheme: light) {
  :root {
    --background-color: #fff;
    --page-width: 70em;
    --primary-color: #151513;
    --font-color: #151513;
    --subtle-primary-color: #151513;
    --block-background-color: #f1f3f4;
    --menu-item-color: #000;
    --menu-item-hover-color: #000;
    --menu-item-alert-bg: #ffffff;
    --menu-item-alert-shadow: #dfe1e7;
    --alert-border-color: #dfe1e7;
    --tertiary-color:: #727680;
  }
}

@media (prefers-color-scheme: dark) {
  :root {
    --background-color: #1e1e1e;
    --primary-color: #157efb;
    --font-color: #dedede;
    --subtle-primary-color: #151513;
    --block-background-color: #323232;
    --menu-item-color: #dedede;
    --menu-item-hover-color: #157efb;
    --menu-item-alert-bg: #151513;
    --menu-item-alert-shadow: #151513;
    --alert-border-color: #000;
    --tertiary-color:: #727680;
  }
}

Conclusion

Adding a Dark mode to your website improves the user experience and doesn't require much effort when you use CSS variables. As soon as prefers-color-scheme support lands in major browsers, users can experience your website in their preferred color scheme, and you will see a surge of "Dark Mode" enabled websites all over the internet.