Recently, I launched my minimal portfolio (the one you are in right now!)

Using React and Gatsby, I have a ton of options to choose from, but I stumbled upon a minimal yet powerful library called use-dark-mode. Ten minutes later, I already had dark mode integrated into my portfolio!

As an Angular developer, I thought it would be nice to have such a library in our ecosystem as well, so I created angular-dark-mode!

Note: Final code and demo available in this stackblitz.

Setup

First, let’s quickly create an angular project we can play with by running:

npx @angular/cli new dark-mode-playground --minimal

Next, add angular-dark-mode:

npm i angular-dark-mode

Or if you prefer yarn:

yarn add angular-dark-mode

Lastly, add the angular-dark-mode.js file to the angular.json scripts section:

{
  "scripts": ["./node_modules/angular-dark-mode/angular-dark-mode.js"]
}

For more on angular-dark-mode.js, see below.

Show Me the Code

angular-dark-mode ships with some configurable options:

OptionDescriptionDefault Value
darkModeClassdark mode css class name'dark-mode'
lightModeClasslight mode css class name'light-mode'
preloadingClasscss class name to flag that element is in preloading state'dark-mode-preloading'
storageKeylocalStorage key to persist dark mode'dark-mode'
elementtarget HTMLElement to set given css classesdocument.body

Given the default options, let’s add some styling to reflect our dark and light modes:

body {
  margin: 0;
}

body:not(.dark-mode-preloading) {
  transition: all 0.3s linear;
}

body.dark-mode {
  background-color: #2f3542;
  color: #f1f2f6;
}

body.light-mode {
  background-color: #f1f2f6;
  color: #2f3542;
}

We want to set styles based on the default configuration above, so we set the styles of dark-mode and light-mode CSS classes.

Also, we want a nice transition between the modes but want to skip the initial transition, so we set it after the preloading phase.

Moving on to app.component.ts, inject DarkModeService and add some text as well as a toggle button:

import { Component } from '@angular/core';
import { DarkModeService } from 'angular-dark-mode';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-root',
  template: `
    <h1>angular-dark-mode is Awesome!</h1>
    <p>Toggle the checkbox to see magic happens!</p>
    <div>
      <input
        type="checkbox"
        [checked]="darkMode$ | async"
        (change)="onToggle()"
      />
    </div>
  `,
  styles: [
    `
      :host {
        min-height: 100vh;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
      }
    `,
  ],
})
export class AppComponent {
  darkMode$: Observable<boolean> = this.darkModeService.darkMode$;

  constructor(private darkModeService: DarkModeService) {}

  onToggle(): void {
    this.darkModeService.toggle();
  }
}

All set! Run the application, and depending on your OS theme, it will open in dark or light mode:

angular-dark-mode example

angular-dark-mode.js

This file has a few purposes:

  1. Persistence — angular-dark-mode saves your preference in localStorage. When the app loads, it retrieves the latest value from localStorage or falls back to the OS’s preference.

  2. Preloading — As we saw, the preloadingClass option can be quite handy when we want to skip the initial transition, so we set the preloadingClass in this file and remove it after initialization.

The angular-dark-mode.js file shipped with the library assumes you are using default options. If you override them, be sure to copy angular-dark-mode.js locally, make the necessary changes, and load the local file in angular.json instead of the library one.


© All rights reserved 2021 | Tal Ohana