> ## Content Index
> Fetch the complete content index at: https://corelab.tech/llms.txt
> Use this file to discover other available public pages before exploring further.

# How to Add a Dark Mode Toggle to Ghost Using Code Injection
- URL: https://corelab.tech/how-to-dark-mode-toggle-ghost/
- Published: 2026-02-10T14:23:17.000Z
- Updated: 2026-10-23T18:25:58.000Z
- Description: Add a persistent dark mode toggle to Ghost's Edition theme using only Code Injection: no theme files or FTP access required, including a fix for the stubborn 'You might also like' section.
- Author: Core Lab Joe
- Tags: Ghost Blog, #2026, #table-of-content

If you are using the **Edition** theme for Ghost, you might have noticed it lacks a native Dark Mode toggle. While Ghost handles themes elegantly, sometimes you want to give your users the choice to save their retinas late at night without having to download, edit, and re-upload the entire theme `.hbs` files.

SERIES 

### Ghost Blog / CMS Hacks

[ 01 Part 1: Customizing & Optimizing Ghost Table of Contents, Scrollspy and more ](https://corelab.tech/ghostblog%5Fcustomization/) [ 02 Part 2: Hacking Ghost CMS Further Adding Mobile-Friendly Breadcrumbs ](https://corelab.tech/ghost-breadcrumbs-guide/) [ 03 Part 3: Dark Mode Toggle How to add Dark Mode Toggle to Ghost CMS (This Post!) ](https://corelab.tech/how-to-dark-mode-toggle-ghost/) 

In this tutorial, I'll show you how to "hack" a persistent Dark Mode toggle into your Ghost site using **only** the Code Injection feature. This means no FTP access required and no risk of breaking your theme files during updates. You can very likely apply the same to other themes that do not have it builtin but ***always be careful***, as I used a combo of vibe-coding with AI and my own elbow grease, examining the dev console in Firefox.

😮

As always - BACKUP your configs (code injections!) before you start playing!

---

## The Strategy

We are going to use a simple three-part strategy:

1. **CSS Variables:** We will define a standard set of color variables (backgrounds, text, borders).
2. **The Class Toggle:** We will use a simple CSS class (`.dark-mode`) applied to the `<html>` tag to overwrite those variables when active.
3. **Local Storage:** We will use a tiny bit of JavaScript to remember the user's preference so they don't have to click the button every time they change pages.

> At the end, this should show up as a little toggle on the bottom left of the screen. It's hidden in mobile mode and/or small screens.

## Step 1: Site Header Injection

First, we need to set up the styles. Navigate to **Settings -> Code Injection** and look at the **Site Header** section.

We are going to add a `<style>` block that does three things:

1. Sets up the default colors.
2. Overrides them when `html.dark-mode` is active.
3. Fixes the "You might also like" section (which is often stubborn).
4. Positions the toggle button in the bottom-left corner (next to the "Top" button if you have one).

Copy and paste this into your **Site Header**:

```html
<style>
    /* =================================================================
       DARK MODE VARIABLES
       ================================================================= */
    
    /* 1. Default (Light) - ensuring defaults are explicit */
    :root {
        --dm-bg: #ffffff;
        --dm-text: #15171a; 
        --dm-secondary: #738a94;
        --dm-border: #e1e1e1;
        --dm-nav-bg: #f9f9f9; 
    }

    /* 2. Dark Mode Overrides */
    html.dark-mode {
        /* Core Ghost Color Overrides */
        --color-base: #e0e0e0;
        --color-secondary-text: #9aa0a6;
        --color-border: #333333;
        --color-bg: #111111;
        
        /* Internal mapping for custom elements */
        --dm-bg: #111111;
        --dm-text: #e0e0e0;
        --dm-secondary: #9aa0a6;
        --dm-border: #333333;
        --dm-nav-bg: #1a1a1a;
        
        /* Update Ghost variables */
        --ghost-accent-bg: #1a1a1a !important;
        --ghost-text-color: #e0e0e0 !important;
    }

    /* 3. Apply Dark Mode Styles to Main Containers */
    html.dark-mode body,
    html.dark-mode .gh-head,
    html.dark-mode .gh-foot {
        background-color: var(--dm-bg) !important;
        color: var(--dm-text) !important;
    }

    /* --- THE "YOU MIGHT ALSO LIKE" FIX --- */
    /* This section (related-wrapper) often stays white by default. 
       We force it (and its children) to accept the dark background. */
    html.dark-mode .gh-readmore,
    html.dark-mode .related-wrapper,
    html.dark-mode .gh-readmore article,
    html.dark-mode .gh-card {
        background-color: var(--dm-bg) !important;
        color: var(--dm-text) !important;
    }
    
    /* Make card internals transparent so the background shows through */
    html.dark-mode .gh-card-wrapper, 
    html.dark-mode .gh-card-content {
        background-color: transparent !important;
    }

    /* Force titles to be white */
    html.dark-mode h1, html.dark-mode h2, html.dark-mode h3, 
    html.dark-mode h4, html.dark-mode h5, html.dark-mode h6,
    html.dark-mode .related-title,
    html.dark-mode .gh-header-title {
        color: #ffffff !important;
    }

    html.dark-mode p,
    html.dark-mode li {
        color: var(--dm-text);
    }

    /* 4. The Toggle Button Styling */
    #dark-mode-toggle {
        position: fixed;
        bottom: 30px;
        left: 100px; /* Adjust this to sit next to your 'Top' button */
        z-index: 9999;
        background: var(--dm-nav-bg);
        color: var(--dm-text);
        border: 1px solid var(--dm-border);
        padding: 0.6em 1em;
        border-radius: 4px;
        font-size: 0.9em;
        cursor: pointer;
        transition: all 0.3s ease;
        box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        display: flex;
        align-items: center;
    }

    html.dark-mode #dark-mode-toggle {
        background: #333;
        color: #ffeb3b; /* Yellow sun icon when active */
        border-color: #555;
    }

    #dark-mode-toggle:hover {
        transform: translateY(-2px);
        box-shadow: 0 4px 8px rgba(0,0,0,0.2);
    }

    /* Hide on mobile to save screen space */
    @media (max-width: 1100px) {
        #dark-mode-toggle {
            display: none; 
        }
    }
</style>

```

## Step 2: Site Footer Injection

Now we need the functionality. This script does a few checks when the page loads:

1. Does the user already have a preference saved in `localStorage`?
2. If not, does their computer system prefer dark mode?
3. If either is true, turn on Dark Mode immediately.

Paste this into your **Site Footer**:

```html
<script>
document.addEventListener('DOMContentLoaded', function () {
    
    // 1. Create the Toggle Button
    const dmToggle = document.createElement("button");
    dmToggle.id = 'dark-mode-toggle';
    dmToggle.setAttribute("aria-label", "Toggle Dark Mode");
    dmToggle.innerHTML = '<span>🌙</span>'; // Default Moon Icon
    document.body.appendChild(dmToggle);

    // 2. Define Toggle Logic
    const htmlEl = document.documentElement;
    const storageKey = 'ghost-theme-preference';

    const setTheme = (theme) => {
        if (theme === 'dark') {
            htmlEl.classList.add('dark-mode');
            dmToggle.innerHTML = '<span>☀️</span>'; // Switch to Sun
            localStorage.setItem(storageKey, 'dark');
        } else {
            htmlEl.classList.remove('dark-mode');
            dmToggle.innerHTML = '<span>🌙</span>'; // Switch to Moon
            localStorage.setItem(storageKey, 'light');
        }
    };

    // 3. Check Preferences on Load
    // Priority: LocalStorage > System Preference > Default Light
    const savedTheme = localStorage.getItem(storageKey);
    const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

    if (savedTheme === 'dark' || (!savedTheme && systemDark)) {
        setTheme('dark');
    }

    // 4. Click Listener
    dmToggle.addEventListener('click', () => {
        if (htmlEl.classList.contains('dark-mode')) {
            setTheme('light');
        } else {
            setTheme('dark');
        }
    });
});
</script>

```

## The "Gotcha" Solved

One of the trickiest parts of hacking the Edition theme is the **"You might also like"** section at the bottom of posts. It uses a container class called `.related-wrapper` (or `.gh-readmore` in older versions) that often ignores global background changes, resulting in white text on a white background.

In the CSS above, you'll notice I included a "Nuclear Fix" block:

```html
html.dark-mode .related-wrapper,
html.dark-mode .related-title {
    background-color: var(--dm-bg) !important;
    color: #ffffff !important;
}

```

This specifically targets that stubborn container and forces it to comply with our dark theme.

## Summary

That's it! You now have a persistent Dark Mode toggle that sits quietly in the bottom left of your screen, remembers your users' preferences, and doesn't require editing a single theme file.

Check out how to lock your environment down and keep your self-hosted website/blog/CMS secure with my [Practical Cybersecurity Guide.](https://corelab.tech/cybersecroadmap/)

---

## ❓ Frequently Asked Questions (FAQ)

### Can I add dark mode to Ghost without editing theme files?

Yes. This method uses only Ghost's built-in Code Injection feature (Settings > Code Injection), pasting CSS into the Site Header and a small JavaScript snippet into the Site Footer. No FTP access or `.hbs` file editing is required, so theme updates won't overwrite it.

### Why does the "You might also like" section stay white in dark mode?

The Edition theme's related-posts container (`.related-wrapper` or `.gh-readmore` in older versions) often ignores global background overrides. It needs its own explicit CSS rule forcing the background and text color, which is included in this guide's CSS block.

### Does the dark mode toggle remember a user's preference?

Yes. The footer script saves the choice to `localStorage` under a `ghost-theme-preference` key, so returning visitors see the same mode again without re-clicking the toggle, and it falls back to the visitor's system-level dark mode preference if nothing is saved yet.

### Will this work on Ghost themes other than Edition?

The same CSS-variable-and-toggle-class approach can likely be adapted to other themes, but the specific selectors (like `.related-wrapper`) are Edition-specific. Always inspect your own theme's CSS in the browser dev tools before assuming a selector will match.

### Where does the dark mode toggle button appear on the page?

It's fixed to the bottom-left of the screen by default, positioned to sit next to a "back to top" button if the theme has one, and it's hidden on mobile/small screens to save space.

---

**🤔Looking for a VPS to host with?**

Core Lab recommends Web Hosting Canada!

[![Web Hosting Canada](https://s.whc.ca/affiliates/banners/green-hosting-leaderboard.png)](https://whc.ca/canadian-web-hosting?aff=7875&ref=corelab.tech)