skip to Main Content

I’m having issues with getting light/dark theme toggle working. The source html, css, Javascript code is below, the html is correct and the button is on the page. Just when i toggle it does nothing: the background page colour doesn’t go from white to black and vice versa. What is missing/wrong. I suspect its the Javascript and/or CSS.
Here’s javascript:

document.addEventListener('DOMContentLoaded', function () {
    const themeToggle = document.getElementById('theme');

    // Function to toggle theme
    function toggleTheme() {
        if (themeToggle.checked) {
            document.documentElement.setAttribute('data-theme', 'dark');
        } else {
            document.documentElement.setAttribute('data-theme', 'light');
        }
    }

    // Event listener for theme toggle change
    themeToggle.addEventListener('change', toggleTheme);
});

Html:

 <label for="theme" class="theme">
   <span class="theme__toggle-wrap">
    <input id="theme"
     class="theme__toggle"
     type="checkbox" role="switch"
     name="theme" value="dark">
    <span class="theme__fill"></span>
    <span class="theme__icon">
     <span
      class="theme__icon-part"></span>
     <span
      class="theme__icon-part"></span>
     <span
      class="theme__icon-part"></span>
     <span
      class="theme__icon-part"></span>
     <span
      class="theme__icon-part"></span>
     <span
      class="theme__icon-part"></span>
     <span
      class="theme__icon-part"></span>
     <span
      class="theme__icon-part"></span>
     <span
      class="theme__icon-part"></span>
    </span>
   </span>
  </label>

Here is the CSS of toggle:

.theme__toggle:checked ~ body {
  background-color: black; /* Dark mode background color */
  color: #fff; /* Text color for dark mode */
}

/* Light mode */
.theme__toggle:not(:checked) ~ body {
  background-color: white; /* Light mode background color */
  color: #000; /* Text color for light mode */
}

i tried simplifying the javascript and css but hasn’t worked. The page by default should be white then turn black when toggle is well toggled and vice versa.

2

Answers


  1. Try this solution please:

    document.addEventListener('DOMContentLoaded', function () {
        const themeToggle = document.getElementById('theme');
    
        // Function to toggle theme
        function toggleTheme() {
            if (themeToggle.checked) {
                document.documentElement.setAttribute('data-theme', 'dark'); 
            } else {
                document.documentElement.setAttribute('data-theme', 'light'); 
            }
        }
    
        // Event listener for theme toggle change
        themeToggle.addEventListener('change', toggleTheme);
    });
    [data-theme="dark"] {
        background-color: black; /* Dark mode background color */
        color: #fff; /* Text color for dark mode */
    }
    
    [data-theme="light"] {
        background-color: white; /* Light mode background color */
        color: #000; /* Text color for light mode */
    }
    <label for="theme" class="theme">
            <span class="theme__toggle-wrap">
                <input id="theme" class="theme__toggle" type="checkbox" role="switch" name="theme">
                <span class="theme__fill"></span>
                <!-- Theme icon spans remain unchanged -->
            </span>
    </label>
    Login or Signup to reply.
  2. The ~ CSS selector is targeting all siblings ahead of .theme__toggle,
    however, .theme__toggleis inside the body and not a sibling before the body tag.

    https://www.w3schools.com/cssref/sel_gen_sibling.php

    replace .theme__toggle:checked ~ body with body:has(.theme__toggle:checked).

    document.addEventListener('DOMContentLoaded', function () {
        const themeToggle = document.getElementById('theme');
    
        // Function to toggle theme
        function toggleTheme() {
            if (themeToggle.checked) {
                document.documentElement.setAttribute('data-theme', 'dark');
            } else {
                document.documentElement.setAttribute('data-theme', 'light');
            }
        }
    
        // Event listener for theme toggle change
        themeToggle.addEventListener('change', toggleTheme);
    });
    body:has(.theme__toggle:checked) {
      background-color: black; /* Dark mode background color */
      color: #fff; /* Text color for dark mode */
    }
    
    /* Light mode */
    body:has(.theme__toggle:not(:checked)) {
      background-color: white; /* Light mode background color */
      color: #000; /* Text color for light mode */
    }
      <label for="theme" class="theme">
       <span class="theme__toggle-wrap">
        <input id="theme"
         class="theme__toggle"
         type="checkbox" role="switch"
         name="theme" value="dark">
        <span class="theme__fill"></span>
        <span class="theme__icon">
         <span
          class="theme__icon-part"></span>
         <span
          class="theme__icon-part"></span>
         <span
          class="theme__icon-part"></span>
         <span
          class="theme__icon-part"></span>
         <span
          class="theme__icon-part"></span>
         <span
          class="theme__icon-part"></span>
         <span
          class="theme__icon-part"></span>
         <span
          class="theme__icon-part"></span>
         <span
          class="theme__icon-part"></span>
        </span>
       </span>
      </label>

    Here’s the support table for :has https://caniuse.com/css-has

    Alternatively, to use your original ~selector, you would need to flatten the input field and style a .container.

    document.addEventListener('DOMContentLoaded', function () {
        const themeToggle = document.getElementById('theme');
    
        // Function to toggle theme
        function toggleTheme() {
            if (themeToggle.checked) {
                document.documentElement.setAttribute('data-theme', 'dark');
            } else {
                document.documentElement.setAttribute('data-theme', 'light');
            }
        }
    
        // Event listener for theme toggle change
        themeToggle.addEventListener('change', toggleTheme);
    });
    .theme__toggle:checked ~ .container {
      background-color: black; /* Dark mode background color */
      color: #fff; /* Text color for dark mode */
    }
    
    /* Light mode */
    .theme__toggle:not(:checked) ~ .container {
      background-color: white; /* Light mode background color */
      color: #000; /* Text color for light mode */
    }
    
    body {
      margin: 0;
      display: flex;
      min-height: 100dvh;
    }
    
    body > [class*=theme] { /* removing from the layout flow */
      position: absolute;
      z-index: 999999;
    }
    
    
    .container {
      width: 100%;
    }
     
    <label for="theme" class="theme">
    </label>
    <input id="theme"
           class="theme__toggle"
           type="checkbox" role="switch"
           name="theme" value="dark">
    <span class="theme__fill"></span>
    <span class="theme__icon">
      <span
            class="theme__icon-part"></span>
      <span
            class="theme__icon-part"></span>
      <span
            class="theme__icon-part"></span>
      <span
            class="theme__icon-part"></span>
      <span
            class="theme__icon-part"></span>
      <span
            class="theme__icon-part"></span>
      <span
            class="theme__icon-part"></span>
      <span
            class="theme__icon-part"></span>
      <span
            class="theme__icon-part"></span>
    </span>
    <div class="container"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search