A Gatsby plugin which handles some of the details of implementing a dark mode theme.
It provides:
- 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.
npm install gatsby-plugin-dark-mode
// gatsby-config.js
module.exports = {
plugins: ['gatsby-plugin-dark-mode'],
}
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.
Here's an example of using ThemeToggler
with a checkbox to toggle the theme:
import React from 'react'
import { ThemeToggler } from 'gatsby-plugin-dark-mode'
class MyComponent extends React.Component {
render() {
return (
<ThemeToggler>
{({ theme, toggleTheme }) => {
// Don't render anything at compile time. Deferring rendering until we
// know which theme to use on the client avoids incorrect initial
// state being displayed.
if (theme == null) {
return null
}
return (
<label>
<input
type="checkbox"
onChange={(e) =>
toggleTheme(e.target.checked ? 'dark' : 'light')
}
checked={theme === 'dark'}
/>{' '}
Dark mode
</label>
)
}}
</ThemeToggler>
)
}
}
The toggled theme will be persisted across visits in localStorage.theme
.
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>
)
}
}
...and in your Typography config if you're using gatsby-plugin-typography
, which is included in the Gatsby Starter Blog:
// typography.js
import './global.css'
import Typography from 'typography'
import Wordpress2016 from 'typography-theme-wordpress-2016'
Wordpress2016.overrideThemeStyles = () => ({
a: {
color: 'var(--textLink)',
},
// gatsby-remark-autolink-headers - don't underline when hidden
'a.anchor': {
boxShadow: 'none',
},
// gatsby-remark-autolink-headers - use theme colours for the link icon
'a.anchor svg[aria-hidden="true"]': {
stroke: 'var(--textLink)',
},
hr: {
background: 'var(--hr)',
},
})
The theme detecting/switching/persistence code and the suggested theming implementation are entirely from the implementation of overreacted.io by Dan Abramov - I'm just publishing them as a plugin to make them easier to use in my own blog, and for reuse by others.