JSFiddle - React, Tailwind, and code Playground

by dumptyd

HTML

<div class="tf-tree tf-gap-sm">
  <ul>
    <li>
      <div>1</div>
      <ul>
        <li>
          <span>1.1</span>
          <ul class="tf-dotted">
            <li><span>1.1.1</span></li>
            <li><span>1.1.2</span></li>
            <li>
              <span>1.1.3</span>
              <ul>
                <li><span>1.1.3.1</span></li>
              </ul>
            </li>
            <li><span>1.1.4</span></li>
            <li><span>1.1.5</span></li>
          </ul>
        </li>
        <li><span>1.2</span></li>
        <li><span>1.3</span></li>
      </ul>
    </li>
  </ul>
</div>

SCSS

body {
  font-family: 'Lato';
  padding: 2rem;
}

$grey: #000;
$scaling_factor: 1;

$node_gap: 1em; /* setting a size in em makes sure that the gap scales too with the scaling factor */
$sm_node_gap: $node_gap * .8;
$lg_node_gap: $node_gap * 1.5;

$connector_width: 1px;
$connector_style: solid;

.tf-tree {
  border: 1px solid #ddd;
  padding: 1rem; /* arbitrary */
  overflow: auto;
  font-size: 1em * $scaling_factor;
  
  * { box-sizing: border-box; }
  li {
    position: relative;
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    align-items: center;
    padding: 0 $node_gap;

    /* node text */
    span:first-child, div:first-child, .tf-node-content:first-child {
      position: relative;
      display: inline-block;
      border: $connector_width solid $grey;
      padding: ($node_gap / 2) $node_gap;
      
      &:before {
        position: absolute;
        height: $node_gap;
        left: calc(50% - #{$connector_width / 2});
        top: calc(#{-$node_gap} - #{$connector_width / 2});
        content: '';
        border-left: $connector_width $connector_style $grey;
      }
      
      &:after {
        position: absolute;
        height: $node_gap;
        left: calc(50% - #{$connector_width / 2});
        top: calc(100% + #{$connector_width / 2});
        content: '';
        border-left: $connector_width $connector_style $grey;
      }
      &:only-child:after { display: none; }
    }
    

    ul {
      display: inline-flex;
      margin: ($node_gap * 2) 0;
    }
    
    li:before {
      position: absolute;
      width: 100%;
      left: calc(0% - #{$connector_width / 2});;
      top: calc(#{-$node_gap} - #{$connector_width / 2});
      content: '';
      border-top: $connector_width $connector_style $grey;
    }
    
    li:first-child:before {
      left: calc(50% - #{$connector_width / 2});
      max-width: calc(100% - 50% + #{$connector_width});
    }
    li:last-child:before {
      left: auto;
      right: calc(50% -...