Hello Dark Mode 🌗

Bony Roopchandani
Aug 31, 2019 · 3 min read

Last week I decided to enable dark mode on my personal website. But instead of keeping just dark mode, or keeping a toggle between normal and dark, I decided to keep it smart, and automatically switch between the modes based on browsers preference.

Image for post
Image for post
Dark Mode Auto Switch based on System Preference

Using CSS media — prefers-color-scheme

The prefers-color-scheme is used to detect if the content is requested to use a light mode or dark mode. It has one of the following values:

  1. no-preference: Indicates that the user has made no preference known to the system.
  2. light: Indicates that the user has notified the system that they prefer a page that has a light theme (dark text on light background).
  3. dark: Indicates that the user has notified the system that they prefer a page that has a dark theme (light text on dark background).

Listening to media query change

To listen for the change, simply create an addListener on the matchMedia object passing prefers-color-scheme: dark as an argument:

componentDidMount {    window
.matchMedia('(prefers-color-scheme: dark)')
.addListener(({ matches }) => {
this.setState({ theme: matches ? 'dark' : 'default' }) })
render() {
// Change the container class name based on theme state

Using Sass features to toggle between modes:

First of all, create the following $themes variable:

$themes: (    default: (        background: $light-theme-bg,        color: $light-theme-color,    ),    dark: (        background: $dark-theme-bg,        color: $dark-theme-color,    ));

We can create a sass mixin which iterates through all the themes and creates a map known as theme-map for each theme-attribute as follows:

@mixin themed() {  @each $theme, $map in $themes {    .theme--#{$theme} & {      $theme-map: () !global;      @each $key, $submap in $map {        $value: map-get(map-get($themes, $theme), '#{$key}');        $theme-map: map-merge($theme-map, ($key: $value)) !global;      }      @content;      $theme-map: null !global;    }  }}

Also, we can create a function, which accepts a key and fetches the corresponding value from the $theme-map:

@function t($key) {    @return map-get($theme-map, $key);}

Lastly, we can include our mixin and function as follows, which will create two declarations for the same class definition (default and dark) and the styles are applied automatically based on the value of the React state.

body {    @include themed() {
background: t('background');

Some screenshots

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store