Tree View

Tree View

A tree view is a hierarchical structure with parent and child nodes that can expand and collapse. Tree views on the web are not common, but they do exist, often to represent a file system or other similar structure of folders and files. Sometimes navigation menus are represented in a tree view type of way. A true ARIA tree view is navigable with the arrow keys on the keyboard instead of the tab key, which is an expected keyboard for native OS applications, but less expected (at least for now) on the web. Support for ARIA tree views is good in some screen readers, but poor in others, so the choice to implement an ARIA tree view should not be made lightly, and may require that you create alternative methods to access the same functionality.



Turn on a screen reader to experience this example in action.

Attribute/Option Description
mode This atrribute/option controls if the tree should be set in an auto mode(AUTO_HEIGHT_MODE) or a maximum height mode (MAX_HEIGHT_MODE). By default, it is set to auto mode.
max_height This atrribute/option specifies the maximum height at which the element should be set. This is a required option if the mode is set to MAX_HEIGHT_MODE.

HTML Source Code


<table class="data">
    <tr>
        <th width="150px" >Attribute/Option</th>
        <th>Description</th>
    </tr>
    <tr>
        <td>mode</td>
        <td>This atrribute/option controls if the tree should be set in an auto mode(AUTO_HEIGHT_MODE) or a maximum height mode (MAX_HEIGHT_MODE). 
            By default, it is set to auto mode.
        </td>
        </tr>
    <tr>
        <td>max_height</td>
        <td>This atrribute/option specifies the maximum height at which the element should be set. This is a required option if the mode is set to MAX_HEIGHT_MODE.
        </td>
    </tr>
</table>
<br/>
<div class="page">
    <div class="body">
      <nav aria-label="Deque University">
        <ul class="treeview-navigation" role="tree" aria-label="Deque University">
          <li role="none">
            <a role="treeitem" href="#home" aria-current="page">
              <span class="label">Home</span>
            </a>
          </li>
          <li role="none">
            <a role="treeitem" aria-expanded="false" aria-owns="id-school-subtree" href="#about">
              <span class="label">
                <span class="icon">
                  <svg xmlns="http://www.w3.org/2000/svg" width="13" height="10" viewBox="0 0 13 10">
                    <polygon points="2 1, 12 1, 7 9"></polygon>
                  </svg>
                </span>
                School Activities
              </span>
            </a>
            <ul id="id-school-subtree" role="group" aria-label="About">
              <li role="none">
                <a role="treeitem" href="#perf" aria-expanded="false" aria-owns="id-perf-subtree">
                  <span class="label">
                    <span class="icon">
                        <svg xmlns="http://www.w3.org/2000/svg" width="13" height="10" viewBox="0 0 13 10">
                          <polygon points="2 1, 12 1, 7 9"></polygon>
                        </svg>
                      </span>
                      Performance
                  </span>
                </a>
                <ul id="id-perf-subtree" role="group" aria-label="Performance">
                    <li role="none">
                      <a role="treeitem" href="#band">
                        <span class="label">Band</span>
                      </a>
                    </li>
                    <li role="none">
                      <a role="treeitem" href="#choir">
                        <span class="label"> Choir </span>
                      </a>
                    </li>
                    <li role="none">
                      <a role="treeitem" href="#theatre">
                        <span class="label">Theater</span>
                      </a>
                    </li>
                  </ul>
              </li>
              
              <li role="none">
                <a role="treeitem" href="#sports" aria-expanded="false" aria-owns="id-sports-subtree">
                    <span class="label">
                      <span class="icon">
                          <svg xmlns="http://www.w3.org/2000/svg" width="13" height="10" viewBox="0 0 13 10">
                            <polygon points="2 1, 12 1, 7 9"></polygon>
                          </svg>
                        </span>
                        Sports
                    </span>
                  </a>
                  <ul id="id-sports-subtree" role="group" aria-label="Sports">
                    <li role="none">
                        <a role="treeitem" href="#falll" aria-expanded="false" aria-owns="id-fall-subtree">
                        <span class="label">
                          <span class="icon">
                              <svg xmlns="http://www.w3.org/2000/svg" width="13" height="10" viewBox="0 0 13 10">
                                <polygon points="2 1, 12 1, 7 9"></polygon>
                              </svg>
                            </span>
                            Fall
                        </span>
                        </a>
                        <ul id="id-fall-subtree" role="group" aria-label="Fall">
                            <li role="none">
                            <a role="treeitem" href="#football">
                                <span class="label">Football</span>
                            </a>
                            </li>
                            <li role="none">
                            <a role="treeitem" href="#soccer">
                                <span class="label">Soccer</span>
                            </a>
                            </li>
                            <li role="none">
                            <a role="treeitem" href="#cross-country">
                                <span class="label">Cross Country</span>
                            </a>
                            </li>
                            <li role="none">
                                <a role="treeitem" href="#cheerleading">
                                <span class="label">Cheerleading</span>
                                </a>
                            </li>
                        </ul>
                    </li>
                    <li role="none">
                      <a role="treeitem" href="#winter" aria-expanded="false" aria-owns="id-winter-subtree">
                        <span class="label">
                          <span class="icon">
                              <svg xmlns="http://www.w3.org/2000/svg" width="13" height="10" viewBox="0 0 13 10">
                                <polygon points="2 1, 12 1, 7 9"></polygon>
                              </svg>
                            </span>
                            Winter
                        </span>
                      </a>
                      <ul id="id-winter-subtree" role="group" aria-label="winter">
                        <li role="none">
                            <a role="treeitem" href="#basketball">
                                <span class="label">Basketball</span>
                            </a>
                            </li>
                            <li role="none">
                            <a role="treeitem" href="#wrestling">
                                <span class="label">Wrestling</span>
                            </a>
                            </li>
                            <li role="none">
                                <a role="treeitem" href="#cheerleading2">
                                <span class="label">Cheerleading</span>
                                </a>
                            </li>                               
                      </ul>
                    </li>
                    <li role="none">
                      <a role="treeitem" href="#spring" aria-expanded="false" aria-owns="id-spring-subtree">
                        <span class="label">
                          <span class="icon">
                              <svg xmlns="http://www.w3.org/2000/svg" width="13" height="10" viewBox="0 0 13 10">
                                <polygon points="2 1, 12 1, 7 9"></polygon>
                              </svg>
                            </span>
                            Spring
                        </span>
                      </a>
                      <ul id="id-spring-subtree" role="group" aria-label="spring">
                        <li role="none">
                        <a role="treeitem" href="#baseball">
                            <span class="label">Baseball</span>
                        </a>
                        </li>
                        <li role="none">
                        <a role="treeitem" href="#track">
                            <span class="label">Track</span>
                        </a>
                        </li>
                      </ul>
                    </li>
                  </ul>
                  
              </li>
            
            </ul>
          </li>
          
          
        </ul>
      </nav>

      
      <section class="main" aria-labelledby="id_website_title id_page_title">
        <h2 class="page_title" id="id_page_title">Deque University</h2>
        <div class="content">
          <p></p>
        </div>
      </section>
    </div>
    
  </div>
  <!---
This component has been adapted from an example provided by the W3C, in accordance with the W3C Software and Document License https://www.w3.org/copyright/software-license-2023/
-->

JavaScript Source Code

class NavigationContentGenerator {
    constructor(siteURL, siteName) {
      this.siteName = siteName;
      this.siteURL = siteURL;
      this.fillerTextSentences = [];
  
      this.fillerTextSentences.push(
        'The content on this page is associated with the <a href="$linkURL">$linkName</a> link for <a href="$siteURL">$siteName</a>.'
      );
      //  this.fillerTextSentences.push('The text content in this paragraph is filler text providing a detectable change of content when the <a href="$linkURL">$linkName</a> link is selected from the menu.  ');
      //  this.fillerTextSentences.push('<a href="$siteURL">$siteName</a> doesn\'t really exist, but the use of an organizational name is useful to provide context for the <a href="$linkURL">$linkName</a> link.  ');
      //  this.fillerTextSentences.push('Since $siteName doesn\'t exist there really is no real content associated with the <a href="$linkURL">$linkName</a> link.');
    }
  
    renderParagraph(linkURL, linkName) {
      var content = '';
      this.fillerTextSentences.forEach(
        (s) =>
          (content += s
            .replace('$siteName', this.siteName)
            .replace('$siteURL', this.siteURL)
            .replace('$linkName', linkName)
            .replace('$linkURL', linkURL))
      );
      return content;
    }
  }
  
  const AUTO_HEIGHT_MODE = 1;       
  const MAX_HEIGHT_MODE = 2; 
  
  class TreeViewNavigation {
    constructor(node, options) {
      var linkURL, linkTitle;
  
      // Check whether node is a DOM element
      if (typeof node !== 'object') {
        return;
      }
  
      document.body.addEventListener('focusin', this.onBodyFocusin.bind(this));
      document.body.addEventListener('mousedown', this.onBodyFocusin.bind(this));
  
      this.treeNode = node;
      this.navNode = node.parentElement;
  
      if (typeof options == "undefined") options = [];
      if (!Array.isArray(options)) options = [];
      if(typeof options["mode"] === "number")
          {
              var val = options["mode"]; 
              if( val == MAX_HEIGHT_MODE)
              {
                  if(typeof options["max_height"] === "number")
                  {
                      this.type = val;
                      this.maxHeight = options["max_height"];
                      this.setMaxHeightMode(this.maxHeight);
                  }
              }
              else
                  this.setAutoMode();
                          
          }
  
  
      this.treeitems = this.treeNode.querySelectorAll('[role="treeitem"]');
      for (let i = 0; i < this.treeitems.length; i++) {
        let ti = this.treeitems[i];
        ti.addEventListener('keydown', this.onKeydown.bind(this));
        ti.addEventListener('click', this.onLinkClick.bind(this));
        // first tree item is in tab sequence of page
        if (i == 0) {
          ti.tabIndex = 0;
        } else {
          ti.tabIndex = -1;
        }
        var groupNode = this.getGroupNode(ti);
        if (groupNode) {
          var span = ti.querySelector('span.icon');
          span.addEventListener('click', this.onIconClick.bind(this));
        }
      }
  
      // Initial content for page
      if (location.href.split('#').length > 1) {
        linkURL = location.href;
        linkTitle = getLinkNameFromURL(location.href);
      } else {
        linkURL = location.href + '#home';
        linkTitle = 'Home';
      }
  
      this.contentGenerator = new NavigationContentGenerator(
        '#home',
        'Deque University'
      );
      this.updateContent(linkURL, linkTitle, false);
  
      function getLinkNameFromURL(url) {
        function capitalize(str) {
          return str.charAt(0).toUpperCase() + str.slice(1);
        }
  
        var name = url.split('#')[1];
        if (typeof name === 'string') {
          name = name.split('-').map(capitalize).join(' ');
        } else {
          name = 'Home';
        }
        return name;
      }
    }
  
    setMaxHeightMode(maxHeight)
    {
      this.treeNode.style.maxHeight = maxHeight+"px";
      this.treeNode.style.overflow = "auto";
    }
    setAutoMode()
    {
      this.treeNode.style.maxHeight = "";
      this.treeNode.style.overflow = "";
    }
    
  
  
    updateContent(linkURL, linkName, moveFocus) {
      var h2Node, paraNodes;
  
      if (typeof moveFocus !== 'boolean') {
        moveFocus = true;
      }
  
      // Update content area
      h2Node = document.querySelector('.page .main h2');
      if (h2Node) {
        h2Node.textContent = linkName;
      }
      paraNodes = document.querySelectorAll('.page .main p');
      paraNodes.forEach(
        (p) =>
          (p.innerHTML = this.contentGenerator.renderParagraph(linkURL, linkName))
      );
  
      // move focus to the content region
      if (moveFocus && h2Node) {
        h2Node.tabIndex = -1;
        h2Node.focus();
      }
  
      // Update aria-current
      this.updateAriaCurrent(linkURL);
    }
  
    getAriaCurrentURL() {
      let url = false;
      let node = this.treeNode.querySelector('[aria-current]');
      if (node) {
        url = node.href;
      }
      return url;
    }
  
    updateAriaCurrent(url) {
      if (typeof url !== 'string') {
        url = this.getAriaCurrentURL();
      }
  
      this.treeitems.forEach((item) => {
        if (item.href === url) {
          item.setAttribute('aria-current', 'page');
          // Make sure link is visible
          this.showTreeitem(item);
          this.setTabIndex(item);
        } else {
          item.removeAttribute('aria-current');
        }
      });
    }
  
    showTreeitem(treeitem) {
      var parentNode = this.getParentTreeitem(treeitem);
  
      while (parentNode) {
        parentNode.setAttribute('aria-expanded', 'true');
        parentNode = this.getParentTreeitem(parentNode);
      }
    }
  
    setTabIndex(treeitem) {
      this.treeitems.forEach((item) => (item.tabIndex = -1));
      treeitem.tabIndex = 0;
    }
  
    getParentTreeitem(treeitem) {
      var node = treeitem.parentNode;
  
      if (node) {
        node = node.parentNode;
        if (node) {
          node = node.previousElementSibling;
          if (node && node.getAttribute('role') === 'treeitem') {
            return node;
          }
        }
      }
      return false;
    }
  
    isVisible(treeitem) {
      var flag = true;
      if (this.isInSubtree(treeitem)) {
        treeitem = this.getParentTreeitem(treeitem);
        if (!treeitem || treeitem.getAttribute('aria-expanded') === 'false') {
          return false;
        }
      }
      return flag;
    }
  
    isInSubtree(treeitem) {
      if (treeitem.parentNode && treeitem.parentNode.parentNode) {
        return treeitem.parentNode.parentNode.getAttribute('role') === 'group';
      }
      return false;
    }
  
    isExpandable(treeitem) {
      return treeitem.hasAttribute('aria-expanded');
    }
  
    isExpanded(treeitem) {
      return treeitem.getAttribute('aria-expanded') === 'true';
    }
  
    getGroupNode(treeitem) {
      var groupNode = false;
      var id = treeitem.getAttribute('aria-owns');
      if (id) {
        groupNode = document.getElementById(id);
      }
      return groupNode;
    }
  
    getVisibleTreeitems() {
      var items = [];
      for (var i = 0; i < this.treeitems.length; i++) {
        var ti = this.treeitems[i];
        if (this.isVisible(ti)) {
          items.push(ti);
        }
      }
      return items;
    }
  
    collapseTreeitem(treeitem) {
      if (treeitem.getAttribute('aria-owns')) {
        var groupNode = document.getElementById(
          treeitem.getAttribute('aria-owns')
        );
        if (groupNode) {
          treeitem.setAttribute('aria-expanded', 'false');
        }
      }
    }
  
    expandTreeitem(treeitem) {
      if (treeitem.getAttribute('aria-owns')) {
        var groupNode = document.getElementById(
          treeitem.getAttribute('aria-owns')
        );
        if (groupNode) {
          treeitem.setAttribute('aria-expanded', 'true');
        }
      }
    }
  
    expandAllSiblingTreeitems(treeitem) {
      var parentNode = treeitem.parentNode.parentNode;
  
      if (parentNode) {
        var siblingTreeitemNodes = parentNode.querySelectorAll(
          ':scope > li > a[aria-expanded]'
        );
  
        for (var i = 0; i < siblingTreeitemNodes.length; i++) {
          siblingTreeitemNodes[i].setAttribute('aria-expanded', 'true');
        }
      }
    }
  
    setFocusToTreeitem(treeitem) {
      treeitem.focus();
    }
  
    setFocusToNextTreeitem(treeitem) {
      var visibleTreeitems = this.getVisibleTreeitems();
      var nextItem = false;
  
      for (var i = visibleTreeitems.length - 1; i >= 0; i--) {
        var ti = visibleTreeitems[i];
        if (ti === treeitem) {
          break;
        }
        nextItem = ti;
      }
      if (nextItem) {
        this.setFocusToTreeitem(nextItem);
      }
    }
  
    setFocusToPreviousTreeitem(treeitem) {
      var visibleTreeitems = this.getVisibleTreeitems();
      var prevItem = false;
  
      for (var i = 0; i < visibleTreeitems.length; i++) {
        var ti = visibleTreeitems[i];
        if (ti === treeitem) {
          break;
        }
        prevItem = ti;
      }
  
      if (prevItem) {
        this.setFocusToTreeitem(prevItem);
      }
    }
  
    setFocusToParentTreeitem(treeitem) {
      if (this.isInSubtree(treeitem)) {
        var ti = treeitem.parentNode.parentNode.previousElementSibling;
        this.setFocusToTreeitem(ti);
      }
    }
  
    setFocusByFirstCharacter(treeitem, char) {
      var start,
        i,
        ti,
        index = -1;
      var visibleTreeitems = this.getVisibleTreeitems();
      char = char.toLowerCase();
  
      // Get start index for search based on position of treeitem
      start = visibleTreeitems.indexOf(treeitem) + 1;
      if (start >= visibleTreeitems.length) {
        start = 0;
      }
  
      // Check remaining items in the tree
      for (i = start; i < visibleTreeitems.length; i++) {
        ti = visibleTreeitems[i];
        if (char === ti.textContent.trim()[0].toLowerCase()) {
          index = i;
          break;
        }
      }
  
      // If not found in remaining slots, check from beginning
      if (index === -1) {
        for (i = 0; i < start; i++) {
          ti = visibleTreeitems[i];
          if (char === ti.textContent.trim()[0].toLowerCase()) {
            index = i;
            break;
          }
        }
      }
  
      // If match was found...
      if (index > -1) {
        this.setFocusToTreeitem(visibleTreeitems[index]);
      }
    }
  
    // Event handlers
  
    onBodyFocusin(event) {
      var tgt = event.target;
  
      if (this.treeNode.contains(tgt)) {
        this.navNode.classList.add('focus');
      } else {
        this.navNode.classList.remove('focus');
        this.updateAriaCurrent();
      }
    }
  
    onIconClick(event) {
      var tgt = event.currentTarget;
  
      if (this.isExpanded(tgt.parentNode.parentNode)) {
        this.collapseTreeitem(tgt.parentNode.parentNode);
      } else {
        this.expandTreeitem(tgt.parentNode.parentNode);
      }
  
      event.preventDefault();
      event.stopPropagation();
    }
  
    onLinkClick(event) {
      var tgt = event.currentTarget;
      this.updateContent(tgt.href, tgt.textContent.trim());
  
      event.preventDefault();
      event.stopPropagation();
    }
  
    onKeydown(event) {
      var tgt = event.currentTarget,
        flag = false,
        key = event.key;
  
      function isPrintableCharacter(str) {
        return str.length === 1 && str.match(/\S/);
      }
  
      if (event.altKey || event.ctrlKey || event.metaKey) {
        return;
      }
  
      if (event.shift) {
        if (
          event.keyCode == this.keyCode.SPACE ||
          event.keyCode == this.keyCode.RETURN
        ) {
          event.stopPropagation();
        } else {
          if (isPrintableCharacter(key)) {
            if (key == '*') {
              this.expandAllSiblingTreeitems(tgt);
              flag = true;
            } else {
              this.setFocusByFirstCharacter(tgt, key);
            }
          }
        }
      } else {
        switch (key) {
          // NOTE: Return key is supported through the click event
          case ' ':
            this.updateContent(tgt.href, tgt.textContent.trim());
            flag = true;
            break;
  
          case 'Up':
          case 'ArrowUp':
            this.setFocusToPreviousTreeitem(tgt);
            flag = true;
            break;
  
          case 'Down':
          case 'ArrowDown':
            this.setFocusToNextTreeitem(tgt);
            flag = true;
            break;
  
          case 'Right':
          case 'ArrowRight':
            if (this.isExpandable(tgt)) {
              if (this.isExpanded(tgt)) {
                this.setFocusToNextTreeitem(tgt);
              } else {
                this.expandTreeitem(tgt);
              }
            }
            flag = true;
            break;
  
          case 'Left':
          case 'ArrowLeft':
            if (this.isExpandable(tgt) && this.isExpanded(tgt)) {
              this.collapseTreeitem(tgt);
              flag = true;
            } else {
              if (this.isInSubtree(tgt)) {
                this.setFocusToParentTreeitem(tgt);
                flag = true;
              }
            }
            break;
  
          case 'Home':
            this.setFocusToTreeitem(this.treeitems[0]);
            flag = true;
            break;
  
          case 'End':
            var visibleTreeitems = this.getVisibleTreeitems();
            this.setFocusToTreeitem(
              visibleTreeitems[visibleTreeitems.length - 1]
            );
            flag = true;
            break;
  
          default:
            if (isPrintableCharacter(key)) {
              if (key == '*') {
                this.expandAllSiblingTreeitems(tgt);
                flag = true;
              } else {
                this.setFocusByFirstCharacter(tgt, key);
              }
            }
            break;
        }
      }
  
      if (flag) {
        event.stopPropagation();
        event.preventDefault();
      }
    }
  }
  
  /**
   * ARIA Treeview example
   *
   * @function onload
   * @description  after page has loaded initialize all treeitems based on the role=treeitem
   */
  
  var treeViews = []; 
  window.addEventListener('load', function () {
    var trees = document.querySelectorAll('nav [role="tree"]');
    var options = [];
    options["mode"] = MAX_HEIGHT_MODE;
    options["max_height"] = 200;
    
  
    for (let i = 0; i < trees.length; i++) {
      //treeViews.push(TreeViewNavigation(trees[i]));
      treeViews.push(new TreeViewNavigation(trees[i], options));
      
    }
  });
  
  //This component has been adapted from an example provided by the W3C, in accordance with the W3C Software and Document License https://www.w3.org/copyright/software-license-2023/

CSS Source Code

.page header {
    border: #005a9c solid 2px;
    background: #005a9c;
    color: white;
    text-align: center;
  }
  
  .page header .title {
    font-size: 2.5em;
    font-weight: bold;
    font-family: serif;
  }
  
  .page header .tagline {
    font-style: italic;
  }
  
  .page footer {
    border: #005a9c solid 2px;
    background: #005a9c;
    font-family: serif;
    color: white;
    font-style: italic;
    padding-left: 1em;
  }
  
  .page .body {
    display: grid;
    grid-template-columns: auto auto;
    border: #eee solid 2px;
  }
  
  .page .body nav {
    margin: 0;
    padding: 6px;
    width: 17em;
    background: #eee;
  }
  
  
  .page .body nav.focus {
    padding: 4px;
    border: 2px solid #005a9c;
  }
  
  .page .body .page {
    margin: 0.25em;
    padding: 0.25em;
}
  
  .page .body .page h2 {
    margin: 0;
    padding: 0;
  }
  
  .page .main {
    padding: 1em;
  }
  
  .treeview-navigation ul,
  .treeview-navigation li {
    margin: 0;
    padding: 0;
  }
  
  .treeview-navigation li li span.label {
    padding-left: 1em;
  }
  
  .treeview-navigation li li li span.label {
    padding-left: 2em;
  }
  
  .treeview-navigation li li li li span.label {
    padding-left: 3em;
  }
  
  .treeview-navigation[role="tree"] {
    margin: 0;
    padding: 0;
    list-style: none;
  }
  
  .treeview-navigation[role="tree"] li {
    margin: 0;
    padding: 0;
    list-style: none;
  }
  
  .treeview-navigation a[role="treeitem"] ul {
    margin: 0;
    padding: 0;
  }
  
  .treeview-navigation
    a[role="treeitem"][aria-expanded="false"]
    + [role="group"] {
    display: none;
  }
  
  .treeview-navigation a[role="treeitem"][aria-expanded="true"] + [role="group"] {
    display: block;
  }
  
  .treeview-navigation a[role="treeitem"] > span svg {
    transform: translate(0, 0);
  }
  
  .treeview-navigation a[role="treeitem"][aria-expanded="false"] > span svg {
    transform: rotate(270deg) translate(2px, 2px);
  }
  
  .treeview-navigation a[role="treeitem"] {
    margin: 0;
    padding: 4px;
    padding-left: 9px;
    text-decoration: none;
    color: #005a9c;
    border: none;
    display: block;
  }
  
  .treeview-navigation a[role="treeitem"][aria-current] {
    border-left: 5px solid #005a9c;
    padding-left: 4px;
    background-color: #ddd;
  }
  
  .treeview-navigation a[role="treeitem"] span.icon svg polygon {
    stroke-width: 2px;
    fill: currentcolor;
    stroke: transparent;
  }
  
  /* disable default keyboard focus styling for treeitems
     Keyboard focus is styled with the following CSS */
  
  .treeview-navigation a[role="treeitem"]:focus {
    outline: 0;
    padding: 2px;
    padding-left: 7px;
    border: 2px #005a9c solid;
  }
  
  .treeview-navigation a[role="treeitem"][aria-current]:focus {
    padding-left: 4px;
    border-left-width: 5px;
  }
  
  .treeview-navigation a[role="treeitem"]:hover {
    background-color: #adddff;
    text-decoration: underline;
    padding-left: 4px;
    border-left: 5px solid #333;
  }
  
  .treeview-navigation a[role="treeitem"] span.icon:hover {
    color: #333;
  }
  
  .treeview-navigation a[role="treeitem"] span.icon svg polygon:hover {
    stroke: currentcolor;
  }

  @media only screen and (max-width: 724px) {
    .page .body {
      grid-template-columns: none;
    }
}

Copy and Paste Full Page Example