htmlcsstreelight

How to make a tree support dark/light mode?


All the pages I have been able to add support for dark/light mode. One page has a tree and everything I tried failed and most changes made the formatting (text, alignment, etc.) change, for the worse. I attempted to change to using ID, class, add a "dark-mode" to the leafs, etc..

When I was successful with dark/light mode, all the text formatting was lost.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>tree test</title>
<meta name="description" content="tree test">

<style>
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;
  }
</style>

</head>
<body>
  <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>

  <script>
  
  // 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');
    });
  </script>

</body>
</html>

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>