Building Pure CSS Trees (part 2)


In our last post, we built a simple pure-CSS tree from a nested list. That tree was horizontally oriented, but what if we wanted a vertically oriented tree? Today, let’s build that.


We left our tree last in this state:

See the Pen css-tree__8 by Stephen Margheim (@smargh) on CodePen.

Let’s take that same HTML, the lessons we learned from our horizontal tree, and start over to build a vertically oriented tree. We can start with our basic .tree styles, our node styles, our our li styles; however, we will need to flip the flex-direction of the lis:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.tree {
  list-style: none;
  
  &, * { margin: 0; padding: 0; }

  li {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  
  span {
    border: 1px solid;
    text-align: center;
    padding: 0.33em 0.66em;
  }
}

See the Pen css-tree-vertical__1 by Stephen Margheim (@smargh) on CodePen.

This gets us heading in the right direction, but we need to have siblings on the same horizontal row. Well, in our HTML, how are is a sibling group defined? As a list (either a ul or ol). And if we want a group of elements to be rendered on the same horizontal row, we can use the flex-direction: row property. So, let’s apply that to all of the lists (both the top most .tree list and any descendant lists):

1
2
3
4
5
6
7
8
9
.tree {
  &, ul, ol {
    list-style: none;
    display: flex;
    flex-direction: row;
  }
  
  // ...
}

See the Pen css-tree-vertical__2 by Stephen Margheim (@smargh) on CodePen.

Now that is starting to look good! Let’s now add our parent-to-child connector, which we want to render down from the bottom of a parent node. Since all we are doing is rotating our tree, we should be able to simply “rotate” the CSS used for our horizontal tree:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.tree {
  // ....
  
  ul, ol {
    padding-top: 2vh;
    position: relative;

    // [connector] parent-to-children
    &::before {
      content: '';
      position: absolute;
      top: 0;
      left: 50%;
      border-left: 1px solid;
      height: 2vh;
    }
  }
}

See the Pen css-tree-vertical__3 by Stephen Margheim (@smargh) on CodePen.

Next, let’s go ahead and add the child-to-parent connector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.tree {
  // ...

  li {
    // ...

    position: relative;
    padding-top: 2vh;
    
    // [connector] child-to-parent
    &::before {
      content: '';
      position: absolute;
      top: 0;
      left: 50%;
      border-left: 1px solid;
      height: 2vh;
    }
  }
}

See the Pen css-tree-vertical__4 by PMACS Team X (@smargh) on CodePen.

Once again, we need to remove any parent-related connectors from the root node:

1
2
3
4
5
6
7
8
9
10
11
12
.tree {
  // ...

  > li {
    padding-top: 0;
    
    &::before,
    &::after {
      display: none;
    }
  }
}

See the Pen css-tree-vertical__5 by Stephen Margheim (@smargh) on CodePen.

Finally, we simply need to add the sibling connector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
.tree {
  // ...

  li {
    // ...

    // [connector] sibling-to-sibling
    &::after {
      content: '';
      position: absolute;
      top: 0;
      border-top: 1px solid;
    }
    // [connector] sibling-to-sibling:last-child
    &:last-of-type::after {
      width: 50%;
      left: 0;
    }
    // [connector] sibling-to-sibling:first-child
    &:first-of-type::after {
      width: 50%;
      right: 0;
    }
    // [connector] sibling-to-sibling:middle-child(ren)
    &:not(:first-of-type):not(:last-of-type)::after {
      width: 100%;
    }
  }
}

See the Pen css-tree-vertical__6 by Stephen Margheim (@smargh) on CodePen.

The only major bit we will add for now is some vertical spacing between children nodes by adding

1
2
  padding-left: .5vw;
  padding-right: .5vw;

to the li selector.

See the Pen css-tree-vertical__7 by Stephen Margheim (@smargh) on CodePen.


Since our vertical tree is really only a “rotation” of our horizontal tree, in our next post, we will consolidate these two components into one .tree component with two modifiers.