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:

.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):

.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:

.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:

.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:

.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:

.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

padding-left: 0.5vw;
padding-right: 0.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.


All posts in this series