Ghost v4 Dark Mode toggle combined with auto mode based on browser preferences saved in a cookie

A mouthful

To put it simple, we're going to cover 3 parts in this guide.

  1. Dark Mode toggle button
  2. Auto Mode based on browser preferences
  3. Save mode as a browser cookie

These 3 parts will be covered in 3 sections of this article.

  1. Lazy Guide: Inject your code through the Ghost admin panel
  2. Hard Coded: Add the code to your theme
  3. Step by step code explanation

Cookies?

Cookies are simple text values saved on the client's computer. The idea is to store information and remember it when you return to the website. Cookies are stored to create a better personal experience.

Without cookies, a website is like a goldfish who loses its memory every time you visit a new page. Once you visit a new page, it doesn’t remember who you are. Learn More

We store our dark mode cookie to remember the dark mode preference of the user. If we don't store a cookie, the mode of preferences will reset on each page load. This means when we try to navigate to a new page, the mode will reset. That's why we store a cookie, to create a better experience for the user.

Lazy Guide

For those of you who don't want to hard code, but rather inject the code through the Ghost admin panel, can follow the "Lazy Guide".

In the Site Header paste the following code.

<style>
    .dark-mode-toggle {
        margin-right: 5px;
    }
</style>

In the Site Footer paste the following code.

<script>
$('div.gh-social').prepend('<a href="#" onclick="themeToggle()" class="dark-mode-toggle">πŸŒ—</a>');
(function() {
    if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches && localStorage.getItem('theme') == null) {
        document.documentElement.classList.add('dark-mode');
        localStorage.setItem("theme", "dark-mode");
    }
    let cookie = localStorage.getItem("theme") || "";
    if(cookie){
    	document.documentElement.classList.add(cookie);
    }
  })();
  
function themeToggle() {
    document.documentElement.classList.toggle("dark-mode");

  let theme = localStorage.getItem("theme");
  if (theme && theme === "dark-mode") {
    localStorage.setItem("theme", "");
  } else {
    localStorage.setItem("theme", "dark-mode");
  }
}
</script>

Hit Save and you're done.

Hard Coded

HTML / Handlebars

First we will add our button that we'll configure later as a toggle.

<a href="#" onclick="themeToggle()" class="dark-mode-toggle">πŸŒ—</a>

Find your current theme folder and open the default.hbs file. Go to line 45 and paste the above code right under the gh-social div.

<div class="gh-social">
    <a href="#" onclick="themeToggle()" class="dark-mode-toggle">πŸŒ—</a>
    {{#if @site.facebook}}
        <a class="gh-social-facebook" href="{{facebook_url @site.facebook}}" title="Facebook" target="_blank" rel="noopener">{{> "icons/facebook"}}</a>
    {{/if}}
    {{#if @site.twitter}}
        <a class="gh-social-twitter" href="{{twitter_url @site.twitter}}" title="Twitter" target="_blank" rel="noopener">{{> "icons/twitter"}}</a>
    {{/if}}
</div>

Styling

Add the following code to your CSS stylesheet. It's a minor change to align the icon with the already existing social icons.

.dark-mode-toggle {
    margin-right: 5px;
}

Javascript

We're going to add 2 functions. The first one will start on page load.

(function() {
    if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches && localStorage.getItem('theme') == null) {
        document.documentElement.classList.add('dark-mode');
        localStorage.setItem("theme", "dark-mode");
    }
    let cookie = localStorage.getItem("theme") || "";
    if(cookie){
    	document.documentElement.classList.add(cookie);
    }
  })();

The second function will be triggered by the toggle button we just created.

function themeToggle() {
    document.documentElement.classList.toggle("dark-mode");

  let theme = localStorage.getItem("theme");
  if (theme && theme === "dark-mode") {
    localStorage.setItem("theme", "");
  } else {
    localStorage.setItem("theme", "dark-mode");
  }
}

That's it!
If you'd like to have a better understanding of the above code keep reading the step by step code explanation.

Step by step code explanation

Page load function

This if clause checks for the preferred color scheme of the browser's settings. It also checks if the cookie hasn't already been set. We do this because otherwise this would override our toggle button.
Then we add the class dark-mode to the html element.
After that we store a cookie called theme with the value dark-mode.

(function() {
    if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches && localStorage.getItem('theme') == null) {
        document.documentElement.classList.add('dark-mode');
        localStorage.setItem("theme", "dark-mode");
    }
    let cookie = localStorage.getItem("theme") || "";
    if(cookie){
    	document.documentElement.classList.add(cookie);
    }
  })()

After the if clause we create a variable called cookie which will get the cookie called theme from the client. We then add the theme variable as a class to the html element. On every page load the html element will be modified with the appropriate theme class.

Trigger function

Our html button has an attribute called onclick which will trigger a Javascript function with that name.

<a href="#" onclick="themeToggle()" class="dark-mode-toggle">πŸŒ—</a>

When triggered we toggle the class dark-mode to the html element.
We then create a variable called theme which will get our cookie called theme.
After that we do a check if the cookie already exists and if it's set to dark-mode.
If it exists, we'll change the cookie and remove the value "dark-mode". This creates an empty cookie called theme. If it's not already set or the theme isn't set to dark-mode, we set the cookie value to dark-mode.

function themeToggle() {
    document.documentElement.classList.toggle("dark-mode");

  let theme = localStorage.getItem("theme");
  if (theme && theme === "dark-mode") {
    localStorage.setItem("theme", "");
  } else {
    localStorage.setItem("theme", "dark-mode");
  }
}