Gatsby Plugin Dark Mode

A Gatsby plugin which handles some of the details of implementing a dark mode theme.

29th July 2022

Time to read: 1 min

Logo

Overview

  • Browser code for toggling and persisting the theme (from Dan Abramov's overreacted.io implementation)
  • Automatic use of a dark mode theme (via the prefers-color-scheme CSS media query) if you've configured your system to use dark colour themes when available.
  • A React component for implementing theme toggling UI in your site.

Install

npm install gatsby-plugin-dark-mode
// gatsby-config.js

module.exports = {
  plugins: ['gatsby-plugin-dark-mode'],
}

How to use

The plugin module exports a ThemeToggler component which takes a children render prop, providing the current theme name and a toggleTheme function to change the theme.

ThemeToggle Component

import React from 'react'
import { ThemeToggler } from 'gatsby-plugin-dark-mode'
// use an icon of choice
import { FaYinYang } from 'react-icons/fa'

const ThemeToggle = () => {

    return (
        <ThemeToggler>
        {({ theme, toggleTheme }) => {
            if (theme == null) return null
            return(
            <label className='cursor-pointer'>
                <input
                type="checkbox"
                hidden
                onChange={e => toggleTheme(e.target.checked ? 'dark' : 'light')}
                checked={theme === 'dark'}
                />{' '}
                <span>
                    <FaYinYang title="Toggle Mode" className={`transition ${theme === 'light' ? "rotate-180" : "rotate-0" }`} />
                </span>
            </label>
            )}
        }
        </ThemeToggler>
    )
}

The toggled theme will be persisted across visits in localStorage.theme.

Add to Navigation

import ThemeToggle from '../themetoggle/'

<ul>
      <li>
        <Link to="/" activeClassName="active">
          Home
        </Link>
      </li>
      // other links
      <li className='flex items-center'>
        <ThemeToggle />
      </li>
</ul>

Implement theming

The default theme names are 'light' and 'dark' - the plugin adds the current theme name to the <body> element's className, so you can use global styles to implement theming.

A nice option is to use CSS variables like so:

/* global.css */

body {
  --bg: white;
  --textNormal: #222;
  --textTitle: #222;
  --textLink: blue;
  --hr: hsla(0, 0%, 0%, 0.2);

  background-color: var(--bg);
}

body.dark {
  -webkit-font-smoothing: antialiased;

  --bg: darkslategray;
  --textNormal: rgba(255, 255, 255, 0.88);
  --textTitle: white;
  --textLink: yellow;
  --hr: hsla(0, 0%, 100%, 0.2);
}

You can then use these variables in your site's components...

class Layout extends React.Component {
  render() {
    return (
      <div
        style={{
          backgroundColor: 'var(--bg)',
          color: 'var(--textNormal)',
          transition: 'color 0.2s ease-out, background 0.2s ease-out',
        }}
      >
        ...
      </div>
    )
  }
}