htmlcsstreelight

How to make elements support dark/light theme mode?


On most pages I was able to add support for dark/light mode.
One page has an expandable tree component, and everything I tried failed and most changes made the formatting (text, alignment, etc.) change for the worse. I attempted to modify the code by using ID, class, add a "dark-mode" to the leaves, etc..

When I was successful with dark/light mode, all the text formatting was lost.
Here's the code I managed to get so far:

// Toggle dark/light mode
const toggleModeButton = document.querySelector('.toggle-mode');
toggleModeButton.addEventListener('click', () => {
  document.body.classList.toggle('dark-mode');
  document.body.classList.toggle('light-mode');
});
body {
  background-color: white;
  color: SlateGray;
}

.dark-mode {
  background-color: black;
  color: SlateGray;
}

/* Tree container style */
.tree {
  max-width: 100%;
  padding: 0;
  list-style: none;
  background: white;
  font-family: 'consolas', monospace;
  font-size: 1.5vw;
  background-color: white;
  color: SlateGray;
  margin: 0;
}

.tree ul {
  list-style: none;
  padding-left: 1em;
}

.tree li {
  padding-left: 0.25em;
  margin: 0.3em 0;
  position: relative;
  cursor: pointer;
}

/* Remove emphasis on text selection */
.tree label {
  cursor: pointer;
  user-select: none;
  display: inline-flex;
  align-items: center;
}

/* Hide native checkboxes */
.tree input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  left: 0;
  top: 0.2em;
  width: 1em;
  height: 1em;
  cursor: pointer;
}

/* Custom triangle toggle icon before label */
.tree input[type="checkbox"]+label::before {
  content: '▶';
  /* Closed node arrow */
  display: inline-block;
  width: 1em;
  margin-right: 6px;
  color: #888;
  transition: transform 0.2s ease;
}

/* Rotate the arrow when checkbox checked (expanded) */
.tree input[type="checkbox"]:checked+label::before {
  transform: rotate(90deg);
  color: #4a90e2;
}

/* Style for leaf nodes (no children) - no arrow */
.tree li.leaf>label::before {
  content: '';
  margin-right: 1.5em; /*align label text with toggles */
}

/* Hide nested UL by default */
.tree input[type="checkbox"]~ul {
  display: none;
}

/* Show nested UL when checkbox checked */
.tree input[type="checkbox"]:checked~ul {
  display: block;
}

/* Accessibility: focus highlight */
.tree input[type="checkbox"]:focus+label {
  outline: 1px solid #4a90e2;
  outline-offset: 2px;
}
<button class="toggle-mode">Toggle Dark/Light Mode</button>

<ul id="treeView" class="tree" role="tree" aria-label="Communications tree view">

  <li role="treeitem" aria-expanded="false">
    <input type="checkbox" id="item-2">
    <label for="item-2">Alpha</label>
    <ul role="group">
      <li class="leaf" role="treeitem">Analog input, output, value</li>
      <li class="leaf" role="treeitem">Binary input, output, value</li>
      <li class="leaf" role="treeitem">Loop</li>
      <li class="leaf" role="treeitem">Multi state input, Multi state output, Multi state value</li>
      <li class="leaf" role="treeitem">Averaging</li>
      <li class="leaf" role="treeitem">Life safety point, Life safety zone</li>
      <li class="leaf" role="treeitem">Read property</li>
      <li class="leaf" role="treeitem">Write property</li>
    </ul>
  </li> 
  <li role="treeitem" aria-expanded="false">
    <input type="checkbox" id="item-6">
    <label for="item-6">Bravo</label>
    <ul role="group">
      <input type="checkbox" id="item-6-1">
      <label for="item-6-1">RTU</label>
      <ul>
        <li class="leaf" role="treeitem">RS-232</li>
        <li class="leaf" role="treeitem">TCP/IP</li>
      </ul>
    </ul>
    <ul role="group">
      <input type="hidden" id="item-6-2">
      <label for="item-6-2">Some river</label>
    </ul>
    <ul role="group">
      <input type="hidden" id="item-6-3">
      <label for="item-6-3">All register types</label>
    </ul>
  </li>

  <li role="treeitem" aria-expanded="false">
    <input type="checkbox" id="item-13">
    <label for="item-13">Charlie</label>
    <ul role="group">
      <li class="leaf" role="treeitem">Some devices</li>
      <li class="leaf" role="treeitem">All register types</li>
    </ul>
  </li>

</ul>


Solution

  • Just add the style you want prefixed with the .dark-mode class:

    .dark-mode .tree {
      background: black;
    }
    

    // Toggle dark/light mode
    const toggleModeButton = document.querySelector('.toggle-mode');
    toggleModeButton.addEventListener('click', () => {
      document.body.classList.toggle('dark-mode');
      document.body.classList.toggle('light-mode');
    });
    body {
      background-color: white;
      color: SlateGray;
      /* text */
    }
    
    .dark-mode {
      background-color: black;
      color: SlateGray;
      /* text */
    }
    
    /* Tree container style */
    .tree {
      max-width: 100%;
      /*   margin: 20px auto;*/
      padding: 0;
      list-style: none;
      background: white;
      font-family: 'consolas';
      /*, tahoma, arial, helvetica, sans-serif*/
      font-size: 1.5vw;
      background-color: white;
      /*    background: white; */
      color: SlateGray;
      margin: 0;
    }
    
    .tree ul {
      list-style: none;
      /*  margin-left: 1em; /* Indentation for nested lists */
      padding-left: 1em;
    }
    
    .tree li {
      padding-left: 0.25em;
      margin: 0.3em 0;
      position: relative;
      cursor: pointer;
    }
    
    /* Remove emphasis on text selection */
    .tree label {
      cursor: pointer;
      user-select: none;
      display: inline-flex;
      align-items: center;
    }
    
    /* Hide native checkboxes */
    .tree input[type="checkbox"] {
      position: absolute;
      opacity: 0;
      left: 0;
      top: 0.2em;
      width: 1em;
      height: 1em;
      cursor: pointer;
    }
    
    /* Custom triangle toggle icon before label */
    .tree input[type="checkbox"]+label::before {
      content: '▶';
      /* Closed node arrow */
      display: inline-block;
      width: 1em;
      margin-right: 6px;
      color: #888;
      transition: transform 0.2s ease;
    }
    
    /* Rotate the arrow when checkbox checked (expanded) */
    .tree input[type="checkbox"]:checked+label::before {
      transform: rotate(90deg);
      color: #4a90e2;
    }
    
    /* Style for leaf nodes (no children) - no arrow */
    .tree li.leaf>label::before {
      content: '';
      margin-right: 1.5em;
      /*align label text with toggles */
    }
    
    /* Hide nested UL by default */
    .tree input[type="checkbox"]~ul {
      display: none;
    }
    
    /* Show nested UL when checkbox checked */
    .tree input[type="checkbox"]:checked~ul {
      display: block;
    }
    
    /* Accessibility: focus highlight */
    .tree input[type="checkbox"]:focus+label {
      outline: 1px solid #4a90e2;
      outline-offset: 2px;
    }
    
    .dark-mode .tree {
      background: black;
    }
    <button class="toggle-mode">Toggle Dark/Light Mode</button>
    
    <ul id="treeView" class="tree" role="tree" aria-label="Communications tree view">
    
      <!-- -->
      <li role="treeitem" aria-expanded="false">
        <input type="checkbox" id="item-2">
        <label for="item-2">Alpha</label>
        <ul role="group">
          <li class="leaf" role="treeitem">Analog input, output, value</li>
          <li class="leaf" role="treeitem">Binary input, output, value</li>
          <li class="leaf" role="treeitem">Loop</li>
          <li class="leaf" role="treeitem">Multi state input, Multi state output, Multi state value</li>
          <li class="leaf" role="treeitem">Averaging</li>
          <li class="leaf" role="treeitem">Life safety point, Life safety zone</li>
          <li class="leaf" role="treeitem">Read property</li>
          <li class="leaf" role="treeitem">Write property</li>
        </ul>
      </li> <!-- -->
    
      <!--  -->
      <li role="treeitem" aria-expanded="false">
        <input type="checkbox" id="item-6">
        <label for="item-6">Bravo</label>
        <ul role="group">
          <input type="checkbox" id="item-6-1">
          <label for="item-6-1">RTU</label>
          <ul>
            <li class="leaf" role="treeitem">RS-232</li>
            <li class="leaf" role="treeitem">TCP/IP</li>
          </ul>
        </ul>
        <ul role="group">
          <input type="hidden" id="item-6-2">
          <label for="item-6-2">Some river</label>
        </ul>
        <ul role="group">
          <input type="hidden" id="item-6-3">
          <label for="item-6-3">All register types</label>
        </ul>
      </li> <!--  -->
    
      <!--  -->
      <li role="treeitem" aria-expanded="false">
        <input type="checkbox" id="item-13">
        <label for="item-13">Charlie</label>
        <ul role="group">
          <li class="leaf" role="treeitem">Some devices</li>
          <li class="leaf" role="treeitem">All register types</li>
        </ul>
      </li> <!-- -->
    
    </ul>