Do you remember those complex, hundreds-of-lines-long CSS files? The days when we had to write body.dark .card, body.dark .header for every single element just to create a Dark Mode? I'm sorry to say this, but if you are still using that method, you are doing unnecessary grunt work.

Web technologies have evolved, and browsers can now carry this burden for us. We are leaving those old, cumbersome methods behind. In this guide, I will show you how to add a smart Dark Mode to your project with effectively one line of code using CSS's new favorite light-dark() function, while using JavaScript strictly as a preference manager.

Plus, we will use a stylish, animated toggle button and save the user's preference to the browser memory (LocalStorage).

If you're ready, let's put your code on a diet.

Why the Old Method is History

The old logic was this: We had a body tag, and we would add a .dark class to it using JavaScript. Then, in our CSS file, we had to define two separate color rules for every single component.

This method became unmanageable as projects grew. If you had 50 different color variables, you had to write an extra 50 lines of code just for dark mode. Now, thanks to the color-scheme property, we tell the browser I am ready for both modes, and leave the rest to CSS variables.

Step 1: HTML Skeleton and Toggle Button

We will keep the design minimalist, but we are keeping the toggle switch—the most critical part of the user experience (UX). This button allows users to make a manual choice independent of their system settings.

Place the following HTML structure in an appropriate spot in your project (usually the Header area):

<div class="theme-switch-wrapper">
    <label class="theme-switch" for="darkModeToggle">
        <input type="checkbox" id="darkModeToggle">
        <div class="slider round">
            <i class="fas fa-sun sun-icon"></i>
            <i class="fas fa-moon moon-icon"></i>
        </div>
    </label>
    <em>Theme</em>
</div>

We are using a simple checkbox logic here. If the checkbox is checked, we activate Night mode; if not, Day mode. Don't forget to include the FontAwesome library (or your preferred icon set) for the icons.

Step 2: CSS Magic (The light-dark Function)

This is the most crucial part. No more media queries or long class definitions. We go to the root (:root) and show our passport to the browser. The color-scheme: light dark; line here is critical. If you don't write this, the light-dark() function below will not work.

:root {
    /* We notify the browser that we support both modes */
    color-scheme: light dark;
    /* SYNTAX: light-dark(DAY_COLOR, NIGHT_COLOR);
       Two color definitions in a single line!
    */
    /* Main Backgrounds */
    --bg-main: light-dark(#f0f4f8, #0b0d17);
    --bg-card: light-dark(#ffffff, #15192b);
    /* Text Colors */
    --text-primary: light-dark(#2d3748, #e2e8f0);
    --text-secondary: light-dark(#718096, #a0aec0);
    /* Accent Colors (Blue for Day, Orange for Night) */
    --accent-color: light-dark(#3182ce, #f6ad55);
    /* Toggle Button Colors */
    --toggle-bg: light-dark(#cbd5e0, #2d3748);
    --toggle-active: var(--accent-color);
}
body {
    background-color: var(--bg-main);
    color: var(--text-primary);
    transition: background-color 0.4s ease, color 0.4s ease;
}

As you can see, for the --bg-main variable, we told the browser: If the mode is light, use #f0f4f8; if it is dark, use #0b0d17. This new capability of CSS is the clearest proof of how far we have come with CSS units and modern features.

CSS for the Toggle Button

Here are the necessary styles for the button to look stylish and transition smoothly:

.theme-switch-wrapper {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.theme-switch {
    position: relative;
    display: inline-block;
    width: 60px;
    height: 30px;
}
.theme-switch input { opacity: 0; width: 0; height: 0; }
.slider {
    position: absolute;
    cursor: pointer;
    top: 0; left: 0; right: 0; bottom: 0;
    background-color: var(--toggle-bg);
    transition: 0.4s;
    border-radius: 30px;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
}
.slider:before {
    position: absolute;
    content: "";
    height: 22px; width: 22px;
    left: 4px; bottom: 4px;
    background-color: white;
    transition: 0.4s;
    border-radius: 50%;
}
input:checked + .slider { background-color: var(--toggle-active); }
input:checked + .slider:before { transform: translateX(30px); }
/* Icon Animations */
.sun-icon, .moon-icon {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.875rem;
    transition: opacity 0.3s ease;
}
.sun-icon { left: 8px; color: #fbbf24; opacity: 1; }
.moon-icon { right: 8px; color: #f3f4f6; opacity: 0; }
input:checked ~ .slider .sun-icon { opacity: 0; }
input:checked ~ .slider .moon-icon { opacity: 1; }

Step 3: JavaScript and LocalStorage Management

The CSS side is ready. When you change your system settings, your site will automatically change colors. However, the user must have the right to say I want night mode during the day.

Here, we are not adding or removing CSS classes with JavaScript. We are directly manipulating the browser's perception (color-scheme) and saving the preference into localStorage. This way, the user's preference is not lost even if they refresh the page.

const toggle = document.getElementById('darkModeToggle');
const root = document.documentElement;
// 1. Check storage when the page loads
const savedTheme = localStorage.getItem('theme-preference');
if (savedTheme) {
    // If there is a preference in memory, apply it
    root.style.colorScheme = savedTheme;
    // If the saved setting is 'dark', check the button
    if (savedTheme === 'dark') {
        toggle.checked = true;
    }
}
// 2. Apply change and save when the button is clicked
toggle.addEventListener('change', function () {
    if (this.checked) {
        // User Selected Dark Mode
        root.style.colorScheme = 'dark';
        localStorage.setItem('theme-preference', 'dark');
    } else {
        // User Selected Light Mode
        root.style.colorScheme = 'light';
        localStorage.setItem('theme-preference', 'light');
    }
});

This method is also highly efficient in terms of performance. You manage the theme of the entire site by simply changing the root style property without heavy DOM manipulations. This way, your admin panel interface respects system settings while allowing manual user control. Plus, our CSS file stays clean, with zero code repetition.