Add Dark mode Theme switch Button to Ghost Blogs -Source Theme

Dark mode light mode Theme Switching button for source Theme, Ghost. It works with Source theme

Add Dark mode Theme switch Button to Ghost Blogs -Source Theme
Light and Dark mode Theme switch Button for Ghost Blog -Source Theme

Hi folks! Writing after a long gap again, going through a very busy schedule right now, I just want to share a quick and effective way to create a light and dark mode switching button for Ghost Blogs. It works with the default Source Theme. And you only need to do a code injection.

Inject this code at your site header and that's it

Go to your site's Admin area > Settings > Code Injection. And paste the code from below.

<!-- Floating Dark Mode Toggle Button with SVG Icons -->
<button class="theme-switch-button" aria-label="Toggle theme" onclick="toggleDarkMode()" style="position: fixed; top: 120px; right: 20px; z-index: 1000; background-color: transparent; border: none; cursor: pointer; padding: 10px;">
    <div class="icon-moon">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-moon" width="24" height="24">
            <path d="M21 12.79A9 9 0 1111.21 3a7 7 0 1010.8 9.79z"></path>
        </svg>
    </div>
    <div class="icon-sun" style="display: none;">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-sun" width="24" height="24">
            <circle cx="12" cy="12" r="5"></circle>
            <line x1="12" y1="1" x2="12" y2="3"></line>
            <line x1="12" y1="21" x2="12" y2="23"></line>
            <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
            <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
            <line x1="1" y1="12" x2="3" y2="12"></line>
            <line x1="21" y1="12" x2="23" y2="12"></line>
            <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
            <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
        </svg>
    </div>
</button>

<script>
    // Function to toggle dark mode and change body background color
    function toggleDarkMode() {
        // Toggle the 'dark-mode' class on the body
        const isDarkMode = document.body.classList.toggle('dark-mode');

        // Change body background color based on mode
        document.body.style.backgroundColor = isDarkMode ? '#191919' : '#ffffff'; // Dark or Light background
        
        // Store the user's choice in local storage
        localStorage.setItem('darkMode', isDarkMode);
        
        // Update the icon visibility based on the current mode
        updateIconVisibility(isDarkMode);

        // Run contrast script to set text color based on the new background color
        setTextColorBasedOnBackground();
    }

    // Function to update icon visibility based on the mode
    function updateIconVisibility(isDarkMode) {
        const moonIcon = document.querySelector('.icon-moon');
        const sunIcon = document.querySelector('.icon-sun');
        
        if (isDarkMode) {
            moonIcon.style.display = 'none';
            sunIcon.style.display = 'block';
        } else {
            moonIcon.style.display = 'block';
            sunIcon.style.display = 'none';
        }
    }

    // Function to dynamically set text color based on background color
    function setTextColorBasedOnBackground() {
        /* Get the current background color of the body element */
        var accentColor = getComputedStyle(document.body).getPropertyValue('background-color');
        
        // Convert RGB to hex format for YIQ calculation
        var rgb = accentColor.match(/\d+/g);
        var r = parseInt(rgb[0]);
        var g = parseInt(rgb[1]);
        var b = parseInt(rgb[2]);
        var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
        
        // Determine if text color should be light or dark
        var textColor = (yiq >= 128) ? 'dark' : 'light';
        
        // Apply the determined text color class to the root element
        document.documentElement.className = `has-${textColor}-text`;
    }

    // Function to check and apply dark mode preference from local storage
    function loadDarkModePreference() {
        const isDarkMode = localStorage.getItem('darkMode') === 'true';
        
        if (isDarkMode) {
            document.body.classList.add('dark-mode');
            document.body.style.backgroundColor = '#191919'; // Set dark background
        } else {
            document.body.classList.remove('dark-mode');
            document.body.style.backgroundColor = '#ffffff'; // Set light background
        }
        
        // Update icon visibility based on the loaded preference
        updateIconVisibility(isDarkMode);
        
        // Set the text color based on the loaded preference
        setTextColorBasedOnBackground();
    }

    // Initial run to load dark mode preference
    loadDarkModePreference();

    // Function to adjust the button position on scroll
    window.addEventListener('scroll', function() {
        const button = document.querySelector('.theme-switch-button');
        const initialPosition = 120; // Initial top position in pixels
        const scrollOffset = window.scrollY; // Get the vertical scroll position

        // Set the top position to initial position minus scroll offset, ensuring it doesn't go above 10px from the top
        const newPosition = Math.max(initialPosition - scrollOffset, 10);
        button.style.top = newPosition + 'px';
    });
</script>

<style>
/* Define text color based on class */
.has-light-text {
    color: #ffffff; /* Text color for light text */
}

.has-dark-text {
    color: #000000; /* Text color for dark text */
}

/* Define background colors based on dark-mode class */
body.dark-mode {
    --background-color: #191919; /* Dark background */
}

body:not(.dark-mode) {
    --background-color: #ffffff; /* Light background */
}

/* Additional styling for the floating button */
.theme-switch-button {
    position: fixed; /* Float button */
    top: 120px; /* Position from top */
    right: 20px; /* Position from right */
    z-index: 1000; /* Make sure it appears above other elements */
    background-color: transparent; /* Transparent background */
    border: none; /* No border */
    cursor: pointer; /* Pointer cursor on hover */
    padding: 10px; /* Button padding */
}

.icon-moon, .icon-sun {
    display: block; /* Display icons */
}
</style>

This code should work with the default Source theme, but please note that it doesn't work with any other themes. I am just using the function "Source" already use to dynamically colour text. And on this blog it self I am using a much more customised version on the this (which you can easily get from the source of my site).

There must be a downside:

And yes, there is one. To change your sites back ground color you need to update this part of the code and not at the design section of your site 🥲.

Yes here:

        // Change body background color based on mode
        document.body.style.backgroundColor = isDarkMode ? '#191919' : '#ffffff'; // Dark or Light background

And here:


        if (isDarkMode) {
            document.body.classList.add('dark-mode');
            document.body.style.backgroundColor = '#191919'; // Set dark background
        } else {
            document.body.classList.remove('dark-mode');
            document.body.style.backgroundColor = '#ffffff'; // Set light background
        }
        

But it should not be a big problem unless you change your site background color too frequently.

Live example:

Link to the demo site.

Conclusion

I hope this short post was helpful to you. And sorry for the infrequent posting, preparing heavily for the residency program (I wish to take up general surgery for my specialization and it is an Indian competitive exam). So wish me luck and hopefully we will meet again soon. And don't forget to comment down below whether this method worked properly or not.