Toggle Dark Mode With Ghost Using Casper Theme

Relax your readers' eyes with a reduced backlight while they read your blog post or content. Having a toggle switch that lets your readers choose between a bright background or darkened background (dark mode) relaxes their eyes at nights or when they're in a dark room

Toggle Dark Mode With Ghost Using Casper Theme

Relax your readers' eyes with a reduced backlight while they read your blog post or content. Having a toggle switch that lets your readers choose between a bright background or darkened background (dark mode) relaxes their eyes at nights or when they're in a dark room. This reduces the amount of light that enters the eyes which, sometimes, lead to tiredness or straining of the eyes. This website is using a toggle switch built by Pavel Rodionov.

This tutorial is aimed at the Casper theme but this will work for any website, as the code only uses CSS and JavaScript.

A toggle switch main purpose is to perform some JavaScript action (in this case) which persist some state or value in the reader's browser. This state is a unique identifier that tells the browser to set the webpage background colour to a darkened colour (I'm in the UK so bear with me by not using the word "color").

Enabling dark mode will require you to take ownership of the theme you're using as we'll need to make edits to our template. This means any updates to the theme you're using, you'll need to add in those changes or updates yourself. Since this tweak is only tiny, you should be ok.

Local Ghost Dev Area

With theme development, a local development of Ghost is needed. Head over to the official documentation to have Ghost running locally or, copy and paste the examples here straight into your theme.

/* 
 * Inside /themes/casper/css/dark.css,
 * paste the css for your toggle switch.
 * This is a copy of mine at the time of this post.
 */

.tumbler__wrapper {
    margin-left: auto;
    width: 50px;
    height: 30px;
    background-color: var(--color-darkmode);
    border-radius: 30px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 6px;
    cursor: pointer;
    display: flex; 
    position: relative;
}

.tumbler {
    position: absolute;
    height: 20px;
    width: 20px;
    border-radius: 50%;
    background-color: #fff;
    transition: transform .5s, background-color .5s;
    will-change: transform;
}

html.dark .tumbler {
    transform: translateX(calc(100% - 2px));
}

Using the toggle switch from the link above, I've made a few changes. The main one is html.dark .tumbler {..}. This sets the toggle in its correct position before the first page paint, if dark mode is already set by the reader. If we use JavaScript, on page load, we'll see the toggle slides to the right. We don't want that.

Non-render blocking JavaScript

There needs to be a way to set a class on the html, before the page loads. To do this we need JavaScript, JS. This JS should not prevent the page from loading. I place this just after the head of the default.hbs file:

<head>
    <script>
        const theme = localStorage.getItem('theme') || null;

        if (theme) {
            document.querySelector('html').classList.add(theme);
        }
    </script>
</head>

When your reader hits your website, for the first time, no class will be set, not until they toggle the switch. We could place this code in the footer or before the last closing body tag, after the page has finished loaded.

<script>
    // Or inside $(document).ready(function () {..}
	document.addEventListener("DOMContentLoaded", function() {

        const addRemove = (currentTheme) => {
            if (currentTheme === 'dark') {
                // Switching from dark to light
                // We instead delete theme key from storage
                localStorage.removeItem('theme');

                // Remove dark class
                document.querySelector('html').classList.remove(currentTheme);

            } else {
                // Add dark mode
                const darkTheme = 'dark';
                localStorage.setItem('theme', darkTheme);
                document.querySelector('html').classList.add(darkTheme);
            }
        }

        const switchTumblerHandler = () => {
            const wrapper = document.querySelector('.tumbler__wrapper')
			
            // Your toggle switch handler function
            // currentTheme should be the same in localStorage
            // A refactor? Your call.
            wrapper.addEventListener('click', () => {
                const currentTheme = document.querySelector('html').classList.value;
                addRemove(currentTheme);
            })
        }

    	switchTumblerHandler()
	});
</script>

I haven't changed much of Pavel's code but eventually I will down the line.

You can safely place the non-rendered-blocking code inside the Site Header and the toggle function in the Site Footer. The toggle html code goes in your theme, inside the default.hbs. Here's mine:

<div class="gh-head-actions">
    <div class="gh-social">
        {{#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>

    {{#unless @member}}
        <a class="gh-head-button" href="#/portal/signup">Subscribe</a>
    {{else}}
        <a class="gh-head-button" href="#/portal/account">Account</a>
    {{/unless}}
    <div class="mw-dark-toggle">
        {{> "dark"}}
    </div>
</div>

I've created a partial called dark.hbs then use it in the file above. Do whatever that suits you.

By toggling the switch, you should see the class name "dark" being added and removed from the html tag. Also inspect the Storage section of your browser console (the Application tab) in Chrome to see the theme value in Local Storage.

To change the background colour:

html.dark body {
    background-color: var(--color-darkmode);
    color: #fff; 
}

html.dark .another-class-to-change {..}

var(--color-darkmode) is specific to the Casper theme but you want black or a dark colour.

We could also refactor the addRemove() but that's now up to you. Hope you've enjoyed this tutorial. ✌🏼