Brocolli

Implementing dark & light themes for your Gatsby website

April 22, 2020

Having a dark mode is cool

Even though a dark mode probably isn’t as good for your eyes as might think, letting the user choose between a dark and a light theme is a nice feature. The biggest driver is aesthetics : dark themed interfaces just look better in my opinion.

light mode vs dark mode

Other reasons are better battery life and less blue light for bedtime reading. If you’re just starting a website, it’s a good idea to implement it right away because the bigger the project gets, the harder it will be to set this up.

#1 : gatsby-styled-components-dark-mode

We are going to take advantage of gatsby plugins to make our life easier.

The first one we are going to be using is called gatsby-styled-components-dark-mode.

A Gatsby plugin that handles injecting a dark and light theme, as well as functionality for toggling between them. It also persists the state of your users’ dark option in their browsers.

Follow install and setup instructions here.

#2 : Syntax highlighting for your code blocks with PrismJS

The tricky part when implementing a dark mode are code blocks. On Brocolli, blog posts are written in Markdown, and parsed and transformed into beautiful html pages with Remark. The code blocks in your markdown file are syntax highlighted with PrismJS.

You need both those Gatsby plugins to make this solution work.

Here is the trick : you need to create custom styles for the code syntax highlighting of your dark and light themes. For example, here are the .css files for the light and the dark themes for this blog. You can download such files here.

Now, the important thing : before every selector in your dark theme .css file, add the following selector : html[displayMode='dark'] .

.token.tag {
  color: #e06c75;
}

thus becomes

html[displayMode='dark'] .token.tag {
  color: #e06c75;
}

Then, in your layout file, get the current theme with useContext :

const themeContext = useContext(ThemeManagerContext);
const displayMode = themeContext.isDark ? "dark" : "light";

and apply it to your document’s head with react-helmet:

<Helmet htmlAttributes={{ displayMode }} />

As you switch the theme on your website, the <html> tag will be updated with the displaymode = "light" or displaymode = "dark" attributes, and the appropriate styling will automatically be applied.

#3 Use the dark theme by default

In your layout file :

const themeContext = useContext(ThemeManagerContext);
useEffect(() =>
    themeContext.toggleDark(window.localStorage.getItem('dark') === "true"),
    //the localStorage item "dark' is handled by `gatsby-styled-components-dark-mode`
[]);

#4 That’s about it

I wrote this article after reading this conversation on spectrum. The original solution comes from tetchi, check out his blog or how he implemented this on his github.

You can also have a look at the implementation of this solution on my blog here.

If you have any questions, google is your friend.

Peace out ✌️