RSS Amplifier

David Carr's Blog · Mar 17, 2025

Implementing a Dark Mode Toggle in Laravel with Tailwind CSS 4

0
Sign in to vote or save

dcblog.dev

TailwindCSS 4 out the box supports prefers-color-scheme CSS media feature, I want class based dark mode support. To enable this add the following to your resources/css/app.css file

@custom-variant dark (&:where(.dark, .dark *));

This allows dark mode when the HTML tag has a class of dark.

Next create a toggle button. These has 2 icons for light mode and dark mode. Light is hidden by default. By adding a hidden class.

I’m using Heroicons that can be installed via blade-ui-kit/blade-heroicons

<button id="theme-toggle" class="p-2">
  <x-heroicon-o-sun id="theme-toggle-light" class="hidden size-5 text-yellow-500" />
  <x-heroicon-o-moon id="theme-toggle-dark" class="size-5 text-gray-900 dark:text-white" />
</button>

Next we need Javascript to handle click events when either icon is pressed.

Create a new file resources/js/dark.js reference it inside the resources/js/app.js file

import './dark';

Inside dark.js add:

document.addEventListener("DOMContentLoaded", applyTheme); // Fires on page load
document.addEventListener("livewire:navigated", applyTheme); // Fires for Livewire navigation
window.addEventListener("popstate", applyTheme); // Detects manual URL changes
function applyTheme() {
    const themeToggle = document.getElementById("theme-toggle");
    const themeToggleLight = document.getElementById("theme-toggle-light");
    const themeToggleDark = document.getElementById("theme-toggle-dark");
    function setTheme(mode) {
        if (mode === "dark") {
            document.documentElement.classList.add("dark");
            localStorage.setItem("theme", "dark");
            if (themeToggleLight && themeToggleDark) {
                themeToggleLight.classList.remove("hidden");
                themeToggleDark.classList.add("hidden");
            }
        } else {
            document.documentElement.classList.remove("dark");
            localStorage.setItem("theme", "light");
            if (themeToggleLight && themeToggleDark) {
                themeToggleDark.classList.remove("hidden");
                themeToggleLight.classList.add("hidden");
            }
        }
    }
    // Check saved theme or use system preference if no saved theme
    const savedTheme = localStorage.getItem("theme");
    if (savedTheme) {
        setTheme(savedTheme); // Apply the saved theme
    } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
        setTheme("dark"); // Apply dark mode based on system preference
    } else {
        setTheme("light");
    }
    // Add event listener for the theme toggle button
    if (themeToggle && !themeToggle.dataset.listenerAdded) {
        themeToggle.addEventListener("click", () => {
            const isDark = document.documentElement.classList.contains("dark");
            setTheme(isDark ? "light" : "dark");
        });
        themeToggle.dataset.listenerAdded = true; // Prevent duplicate event listeners
    }
}

Now rebuild your assets npm run build Now clicking on either icon should toggle the dark / light mode. Also the site will honor the visitors system preferences by default so if they are using dark mode, dark mode will load by default.

Subscribe to my newsletter for the latest updates on my books and digital products.

Read the original on dcblog.dev

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.