JSFiddle - React, Tailwind, and code Playground

by Lazaro Contreras

HTML

<h3 id="tree_label">
  File Viewer
</h3>
<ul role="tree" aria-labelledby="tree_label">
  <li role="treeitem" aria-expanded="false">
    <span>
      Projects
    </span>
    <ul role="group">
      <li role="treeitem" class="doc">
        project-1.docx
      </li>
      <li role="treeitem" class="doc">
        project-2.docx
      </li>
      <li role="treeitem" aria-expanded="false">
        <span>
          Project 3
        </span>
        <ul role="group">
          <li role="treeitem" class="doc">
            project-3A.docx
          </li>
          <li role="treeitem" class="doc">
            project-3B.docx
          </li>
          <li role="treeitem" class="doc">
            project-3C.docx
          </li>
        </ul>
      </li>
      <li role="treeitem" class="doc">
        project-4.docx
      </li>
      <li role="treeitem" aria-expanded="false">
        <span>
          Project 5
        </span>
        <ul role="group">
          <li role="treeitem" class="doc">
            project-5A.docx
          </li>
          <li role="treeitem" class="doc">
            project-5B.docx
          </li>
          <li role="treeitem" class="doc">
            project-5C.docx
          </li>
          <li role="treeitem" class="doc">
            project-5D.docx
          </li>
          <li role="treeitem" class="doc">
            project-5E.docx
          </li>
          <li role="treeitem" class="doc">
            project-5F.docx
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li role="treeitem" aria-expanded="false">
    <span>
      Reports
    </span>
    <ul role="group">
      <li role="treeitem" aria-expanded="false">
        <span>
          report-1
        </span>
        <ul role="group">
          <li role="treeitem" class="doc">
            report-1A.docx
          </li>
          <li role="treeitem" class="doc">
            report-1B.docx
          </li>
          <li role="treeitem" class="doc">
            report-1C.docx
       ...

CSS

ul[role="tree"] {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 120%;
}

[role="treeitem"].doc::before {
  content: url(../images/file.png);
}

[role="treeitem"][aria-expanded="false"] > ul {
  display: none;
}

[role="treeitem"][aria-expanded="true"] > ul {
  display: block;
}

[role="treeitem"][aria-expanded="false"] > span::before {
  content: url(../images/closed.png);
}

[role="treeitem"][aria-expanded="true"] > span::before {
  content: url(../images/open.png);
}

[role="treeitem"],
[role="treeitem"] span {
  width: 9em;
  margin: 0;
  padding: 0.125em;
  border: 2px transparent solid;
  display: block;
}

/* disable default keyboard focus styling for treeitems
   Keyboard focus is styled with the following CSS */
[role="treeitem"]:focus {
  outline: 0;
}

[role="treeitem"].focus,
[role="treeitem"] span.focus {
  border-color: black;
  background-color: #eee;
}

[role="treeitem"].hover,
[role="treeitem"] span:hover {
  background-color: #ddd;
}

JavaScript

/*
*   This content is licensed according to the W3C Software License at
*   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
*
*   File:   Treeitem.js
*
*   Desc:   Setup click events for Tree widget examples
*/

/**
 * ARIA Treeview example
 * @function onload
 * @desc  after page has loaded initialize all treeitems based on the role=treeitem
 */


  
/*
*   This content is licensed according to the W3C Software License at
*   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
*
*   File:   Treeitem.js
*
*   Desc:   Treeitem widget that implements ARIA Authoring Practices
*           for a tree being used as a file viewer
*/

/*
*   @constructor
*
*   @desc
*       Treeitem object for representing the state and user interactions for a
*       treeItem widget
*
*   @param node
*       An element with the role=tree attribute
*/

var Treeitem = function (node, treeObj, group) {

  // Check whether node is a DOM element
  if (typeof node !== 'object') {
    return;
  }

  node.tabIndex = -1;
  this.tree = treeObj;
  this.groupTreeitem = group;
  this.domNode = node;
  this.label = node.textContent.trim();

  if (node.getAttribute('aria-label')) {
    this.label = node.getAttribute('aria-label').trim();
  }

  this.isExpandable = false;
  this.isVisible = false;
  this.inGroup = false;

  if (group) {
    this.inGroup = true;
  }

  var elem = node.firstElementChild;

  while (elem) {

    if (elem.tagName.toLowerCase() == 'ul') {
      elem.setAttribute('role', 'group');
      this.isExpandable = true;
      break;
    }

    elem = elem.nextElementSibling;
  }

  this.keyCode = Object.freeze({
    RETURN: 13,
    SPACE: 32,
    PAGEUP: 33,
    PAGEDOWN: 34,
    END: 35,
    HOME: 36,
    LEFT: 37,
    UP: 38,
    RIGHT: 39,
    DOWN: 40
  });
};

Treeitem.prototype.init = function () {
  this.domNode.tabIndex = -1;

  if (!this.domNode.getAttribute('role')) {
    this.domNode.setAttribute('role', 'treeitem');
  }

 ...