Hugo theme Dark Mode toggle, Bootstrap & Disqus dark mode bug fix

27 Oct 2023

Short description of how I made my dark mode toggle switch for my Hugo theme using Bootstrap’s dark mode.

HTML changes

So this is my navbar-partial.html for Hugo using Bootstrap’s navbar layout.

<nav class="navbar navbar-expand-md navbar-custom">
    <div class="container-fluid">
    <a class="navbar-brand" href="/"><img height="28" src="/raddinox-blog-title-2-small.png" /></a>
    {{ if site.IsServer }}<span style="color: gray;">running: Hugo server</span>{{ end }}
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse justify-content-end" id="navbarSupportedContent">
        <ul class="navbar-nav ml-auto">
            {{- $sec := .Page.Section }}{{ $file := .File.TranslationBaseName -}}
                {{ range.Site.Menus.main.ByWeight }}{{ $base := path.Base .URL }}
            <li class="nav-item"><a class="nav-link" href="{{ .URL }}">{{ .Name }}</a></li>
                {{- end }}

            <!-- Custom added navbar item for Dark mode toggle -->
            <li class="nav-item"><a href="javascript:void(0)" id="btn_toggleDarkMode" class="nav-link bi-moon-stars" onclick="toggleDarkMode()"></a></li>
    </div>
    </div>
</nav>

After Hugo’s template auto adding of navbar items I manually added an item for my dark mode switch button

<a href="javascript:void(0)" id="btn_toggleDarkMode" class="nav-link bi-moon-stars" onclick="toggleDarkMode()"></a>

Now I have a new item with the Bootstrap icon bi-moon-stars and clicking it will call my toggleDarkMode() function. This will change the theme and also change the toggle button to bi-sun

Javascript

To change the theme to dark mode is very simple thanks to Bootstrap theming feature. You just need to change the data-bs-theme on the HTML tag between dark and light and Bootstrap will use some CSS magic to change the theme for you.

This works great, until you change page or refresh a page. Because it will not remember the state of the data-bs-theme on the HTML tag. So I decided to store the current theme mode in a sessionStorage and on page load it will restore the stored theme from the sessionStorage. The sessionStorage is not a Cookie and will only remember the state while the page is open. Closing the browser/tab will destroy the sessionStorage and the use have to change again on next visit. This can be changed to use Cookies so it will remember the settings between sessions.

In my Javascript I added the feature to change my dark mode toggle icon to match the light/dark theme as well.

function toggleDarkMode(e) {
    let theme = document.documentElement.getAttribute('data-bs-theme');

    console.log('Theme: ' + theme + " chainging theme");

    if ( theme == "dark") {
        setMode("light");
    } else {
        setMode("dark");
    }
}

function setMode(mode) {
    console.log('Set theme ' + mode);

    // Set Bootstrap theme
    document.documentElement.setAttribute('data-bs-theme', mode);

    // Change dark mode toggle icon
    let btn = document.getElementById("btn_toggleDarkMode");
    if ( mode == "dark") {
        btn.classList.add('bi-moon-stars');
        btn.classList.remove('bi-sun');
    } else {
        btn.classList.remove('bi-moon-stars');
        btn.classList.add('bi-sun');
    }

    // Save current theme mode in sessionStorage
    sessionStorage.setItem("theme-mode", mode);
}

// Load stored theme to remember user settings
function onLoad() {
    let theme = sessionStorage.getItem("theme-mode");
    if( theme ) {
      setMode(theme);
    }
}

window.addEventListener('DOMContentLoaded', onLoad);

Disqus theming issue

So everything was working fine, until I tried my page online with Disqus etc. enabled. This is when I discovered that Disqus part looked “zoomed in” because I could not see everything.

Bugged Disqus
I managed to track it down to Bootstrap’s theming

<html data-bs-theme="dark">

With that attribute Disqus would fail to render properly. Now I realised it was not “zoomed in” the colors of text was wrong so I could not see everything.

Finally I tracked it down to this part of the code in Bootstrap

[data-bs-theme="dark"] {
    color-scheme: dark;

So in my CSS file that is loaded after Bootstrap I unset that value and it will work…

[data-bs-theme="dark"] {
    color-scheme: unset;
}

Unless you change the theme. Because Disqus will only update it’s own theme when it’s loading.

To fix this we need to reload Disqus and this was actually easy. In my toggleDarkMode function I added a bit of code to reload Disqus when the toggle button is clicked.

function toggleDarkMode(e) {
    let theme = document.documentElement.getAttribute('data-bs-theme');

    console.log('Theme: ' + theme + " chainging theme");

    if ( theme == "dark") {
        setMode("light");
    } else {
        setMode("dark");
    }

    // Need to reload Disqus when dark/light mode has been changed
    if (typeof DISQUS !== "undefined")
    {
        DISQUS.reset({ reload: true });
    }
}

And finally my Hugo theme is working.