How to Create a Toggle Switch for Dark Mode with CSS and JavaScript

Dark mode has become a must-have feature in modern web design, offering users the flexibility to switch between light mode and dark mode based on their preference. Many popular websites and applications, including Twitter, YouTube, and Reddit, now offer a dark mode option, enhancing usability and reducing eye strain, especially in low-light environments. A toggle switch provides an intuitive way for users to instantly switch between light and dark themes without reloading the page.

By implementing this feature, you can improve user experience, accessibility, and energy efficiency, especially for OLED and AMOLED screen users.

Step 1 - Setting Up the HTML Structure

To create a dark mode toggle switch, we will use a simple checkbox input inside a <label> element. This allows us to style the switch effectively with CSS while keeping it accessible and functional.

HTML Structure for the Toggle Switch

Below is the basic HTML markup needed for our toggle switch:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dark Mode Toggle</title>
    <link rel="stylesheet" href="/styles.css">
</head>
<body>
    <div class="container">
        <label class="toggle-switch">
            <input type="checkbox" id="dark-mode-toggle">
            <span class="slider"></span>
        </label>
    </div>

    <script src="/script.js"></script>
</body>
</html>

The HTML Elements

  • <label>: The toggle switch is wrapped in a <label> to make it clickable.
  • <input type="checkbox">: A hidden checkbox controls the toggle state.
  • <span class="slider">: This is the visible part of the switch, which we will style in the next step.
  • id="dark-mode-toggle": The unique ID allows JavaScript to detect when the switch is toggled.
  • External CSS (styles.css) and JavaScript (script.js) files are linked to style and control the functionality of the switch.

Step 2 - Styling the Toggle Switch with CSS

Now it's time to make our toggle switch look like a real button using CSS. We'll create a sleek, modern design and add a smooth transition effect to enhance the user experience.

Adding Basic Styles to the Toggle Switch

Create a new CSS file (styles.css) and add the following styles:

/* General Styles */
body {
    font-family: Arial, sans-serif;
    background-color: #ffffff;
    color: #333;
    transition: background-color 0.3s, color 0.3s;
    text-align: center;
    padding: 50px;
}

/* Centering the toggle */
.container {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

/* Toggle Switch Styling */
.toggle-switch {
    position: relative;
    width: 50px;
    height: 25px;
    display: inline-block;
}

/* Hide default checkbox */
.toggle-switch input {
    display: none;
}

/* The slider (switch button) */
.slider {
    position: absolute;
    cursor: pointer;
    width: 100%;
    height: 100%;
    background-color: #ccc;
    border-radius: 25px;
    transition: background-color 0.3s;
}

/* The circular button inside the switch */
.slider::before {
    content: "";
    position: absolute;
    width: 20px;
    height: 20px;
    background-color: white;
    border-radius: 50%;
    top: 50%;
    left: 5%;
    transform: translateY(-50%);
    transition: transform 0.3s;
}

/* When the switch is toggled (checked state) */
input:checked + .slider {
    background-color: #4caf50;
}

/* Move the slider when checked */
input:checked + .slider::before {
    transform: translate(22px, -50%);
}

How This CSS Works

  • The .toggle-switch class creates the overall switch structure.
  • The checkbox input is hidden so that only the styled slider is visible.
  • The .slider acts as the visible switch, with a rounded pill shape.
  • The .slider::before represents the circular toggle button, which moves when clicked.
  • Using transition: 0.3s; ensures a smooth animation effect when switching between light mode and dark mode.

Step 3 - Writing JavaScript to Enable Dark Mode

Now that we have a styled toggle switch, we need to make it functional using JavaScript. Our script will:

  • Detect when the switch is toggled
  • Apply dark mode styles by adding/removing a CSS class
  • Store the user’s preference in localStorage so the site remembers their choice

Adding Dark Mode Styles to CSS

Before we dive into JavaScript, let’s define dark mode styles in styles.css:

/* Dark Mode Styles */
.dark-mode {
    background-color: #1e1e1e;
    color: #ffffff;
}

/* Change toggle switch color in dark mode */
.dark-mode .slider {
    background-color: #2196F3;
}

JavaScript to Handle Dark Mode Toggle

Create a new JavaScript file (script.js) and add the following code:

// Select the toggle switch
const toggleSwitch = document.getElementById("dark-mode-toggle");

// Function to enable dark mode
function enableDarkMode() {
    document.body.classList.add("dark-mode"); // Apply dark mode styles
    localStorage.setItem("darkMode", "enabled"); // Save preference
}

// Function to disable dark mode
function disableDarkMode() {
    document.body.classList.remove("dark-mode"); // Remove dark mode styles
    localStorage.setItem("darkMode", "disabled"); // Save preference
}

// Check if the user has a preference saved
if (localStorage.getItem("darkMode") === "enabled") {
    enableDarkMode();
    toggleSwitch.checked = true; // Ensure the toggle remains in the correct state
}

// Listen for toggle switch changes
toggleSwitch.addEventListener("change", () => {
    if (toggleSwitch.checked) {
        enableDarkMode();
    } else {
        disableDarkMode();
    }
});

How This JavaScript Works

  1. Selects the toggle switch using document.getElementById().
  2. Defines functions to enable and disable dark mode by adding/removing the .dark-mode class from <body>.
  3. Stores the user’s preference in localStorage so the setting is remembered.
  4. Checks localStorage on page load to restore the user’s preference.
  5. Listens for toggle changes and applies the correct mode.

Step 4 - Improving the Dark Mode Styles

Let’s improve the user experience by refining the dark mode colours, contrast, and readability. A well-designed dark mode should reduce eye strain, maintain good visibility, and ensure that all elements remain legible.

Improving Background and Text Colours

A good dark mode isn’t just about switching the background to black—it should use shades of dark gray instead of pure black (#000000), which can cause eye strain.

  • Use softer dark backgrounds: #1e1e1e, #2b2b2b, or #121212
  • Use off-white text for readability: #e0e0e0 or #cfcfcf instead of pure white
  • Ensure buttons and links stand out in dark mode

Updated Dark Mode Styles

Add the following refinements to styles.css:

/* Improved Dark Mode Styles */
.dark-mode {
    background-color: #1e1e1e; /* Soft dark background */
    color: #e0e0e0; /* Light gray text for readability */
}

/* Improve header and navigation readability */
.dark-mode h1, 
.dark-mode h2, 
.dark-mode h3 {
    color: #ffffff;
}

/* Adjust button styles for dark mode */
.dark-mode button {
    background-color: #333;
    color: #ffffff;
    border: 1px solid #555;
}

.dark-mode button:hover {
    background-color: #444;
}

/* Improve links in dark mode */
.dark-mode a {
    color: #4db8ff; /* Soft blue */
}

.dark-mode a:hover {
    color: #66ccff; /* Lighter blue on hover */
}

/* Adjust the toggle switch color for better contrast */
.dark-mode .slider {
    background-color: #4db8ff;
}

Contrast and Accessibility

🔹 Test contrast ratios to ensure that text remains readable against dark backgrounds.
🔹 Use WebAIM’s Contrast Checker to verify colour accessibility.
🔹 Use box shadows subtly to create visual separation instead of relying on bright outlines.

Example:

/* Adding soft shadows for depth in dark mode */
.dark-mode .card {
    background-color: #252525;
    box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.3);
}

Adding Smooth Transitions for a Better Experience

Right now, when the user toggles dark mode, the changes happen instantly, which can feel abrupt. To create a smoother experience, we can add a fade effect.

Add this to your CSS file:

/* Smooth transition for dark mode */
body {
    transition: background-color 0.5s ease, color 0.5s ease;
}

With these refinements, dark mode will feel more polished and user-friendly. The improved contrast, button styles, and smooth transitions will enhance readability and create a better browsing experience for all users.

Step 5 - Testing and Debugging

Now that our dark mode toggle switch is fully implemented, it's important to test it across different browsers and devices to ensure smooth functionality. A well-tested dark mode improves user experience, accessibility, and site reliability.

Testing Across Browsers

Not all browsers handle JavaScript and CSS transitions the same way, so it's important to test in:

  • Google Chrome – Most widely used browser with full JavaScript support
  • Mozilla Firefox – Strong CSS support but may handle localStorage differently
  • Safari – Can sometimes have issues with localStorage
  • Microsoft Edge – Based on Chromium, but still requires testing
  • Opera & Brave – Less common but growing in usage

How to Test:

  1. Open your website in each browser.
  2. Toggle dark mode on and off multiple times.
  3. Refresh the page to check if localStorage remembers user preference.
  4. Inspect elements using Developer Tools (F12 > Console/Storage/Elements).

Testing on Mobile Devices

Mobile devices render pages differently, so ensure dark mode works on:

  • Android (Chrome, Firefox, Samsung Internet)
  • iOS (Safari, Chrome)
  • Tablet devices (iPads, Android tablets)

How to Test on Mobile:

  1. Open your site on different devices (or use Chrome DevTools > Toggle Device Toolbar for simulated testing).
  2. Check if the toggle switch is touch-friendly and works properly.
  3. Confirm CSS styling remains readable in dark mode.
  4. Refresh the page to ensure dark mode persists after reload.

Common Issues and Fixes

Issue: Dark mode doesn't persist after page refresh
Fix: Ensure localStorage is working and darkMode preference is set properly.

console.log(localStorage.getItem("darkMode")); // Debugging output

Issue: Dark mode toggle appears glitchy when switching
Fix: Add transition: 0.3s ease-in-out; to body in CSS for a smooth effect.

Issue: colours look too dark or unreadable in dark mode
Fix: Adjust background and text colours using contrast checkers (WebAIM Contrast Checker).

Issue: Dark mode toggle not working on some mobile browsers
Fix: Ensure JavaScript runs after the page loads by wrapping it in.

document.addEventListener("DOMContentLoaded", function() {
    // Your dark mode script here
});

Final Testing Steps

  • Check performance impact – Dark mode should not slow down page loads.
    Verify accessibility – Test with screen readers to ensure usability.
    Cross-test in incognito mode – Some browsers block localStorage in private browsing.

Further Enhancements

This dark mode toggle is just the beginning! Feel free to experiment, modify, and implement it in your own projects to enhance usability and design.

Need a Helping Hand with Your Project?

Whether you need continuous support through our Flexible Retainer Plans or a custom quote, we're dedicated to delivering services that align perfectly with your business goals.

Please enter your name

Please enter your email address

Contact by email or phone?

Please enter your company name.

Please enter your phone number

What is your deadline?

Please tell us a little about your project