A few weeks ago I needed to implement a tree-like structure for a front-end application. Since we’re using Vue.js for templating and rendering, this was pretty easy to do. However, one still needs to understand the implementation in basic HTML and CSS since these are used for the <template>
and <style>
sections of the Vue components. Let’s start with a basic tree-like structure in simple (very simple) HTML5:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Simple Tree</title>
<meta name="description" content="simple trees with HTML and CSS">
</head>
<body>
<ul>
<li>Root
<ul>
<li>Child-1
<ul>
<li>Child-3</li>
<li>Child-4</li>
</ul>
</li>
<li>Child-2
<ul>
<li>Child-5</li>
<li>Child-6</li>
</ul>
</li>
</ul>
</li>
</ul>
</body>
</html>
The HTML in the snippet above renders as:
Now if we add CSS classes to these elements to describe the items into typical tree terms (e.g. ‘nodes’, ‘roots’, etc.) we can start using CSS selectors to make the tree more apparent. When adding these classes, the HTML listing above becomes:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Simple Tree</title>
<meta name="description" content="simple trees with HTML and CSS">
<link rel="stylesheet" href="simple_tree.css">
</head>
<body>
<ul class="root">
<li class="tree-node"><a href="#" class="node-name">Root</a>
<ul>
<li class="tree-node"><a href="#" class="node-name">Child-1</a>
<ul>
<li class="tree-node"><a href="#" class="node-name">Child-3</a></li>
<li class="tree-node"><a href="#" class="node-name">Child-4</a></li>
</ul>
</li>
<li class="tree-node"><a href="#" class="node-name">Child-2</a>
<ul>
<li class="tree-node"><a href="#" class="node-name">Child-5</a></li>
<li class="tree-node"><a href="#" class="node-name">Child-6</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</body>
</html>
The contents of the CSS file simple_tree.css specified on line 9 in the listing above are:
body {
font-family: sans-serif;
font-weight: 400;
color: #313131;
line-height: 1.3;
text-align: left;
}
.root {
list-style: none;
margin: 0;
}
.root > .tree-node::before,
.root > .tree-node::after {
display: none;
}
.node-name {
color: darkblue;
text-decoration: none;
}
ul {
display: block;
list-style: none;
line-height: 1.3;
margin: 0 0 0 1rem;
padding-left: 0;
}
ul .tree-node {
display: block;
padding-left: 1rem;
position: relative;
}
/* Makes an 'L' shape before the node. */
.tree-node::before {
content: '';
position: absolute;
display: block;
left: -0.7rem;
top: 0;
height: 0.8rem;
width: 1.4rem;
border-left: 1px solid black;
border-bottom: 1px solid black;
}
/* Makes a vertical line that connects the 'L's before each node. */
.tree-node::after {
content: '';
position: absolute;
display: block;
left: -0.7rem; /* Must line up with li::before['left']. */
bottom: -0.3rem;
height: 100%;
border-left: 1px solid black;
}
/* Don't draw the vertical line after the last node.
It goes all the way to the bottom of the <ul> otherwise. */
.tree-node:last-child::after {
display: none;
}
The styled HTML and the accompanying CSS from above generate the resulting tree:

The CSS pseudo classes ::before
and ::after
provide the orthogonal connecting lines from parent to child. The ::before
class with the border-left
and border-bottom
specification makes an “L” shape in the indented space before each child. The ::after
class only specifies border-left
which draws a connecting line from the bottom of the “L” shape of one child to the top of the “L” shape before the following child. However, note that the last CSS element, .tree-node:last-child::after
has the rule display: none;
—meaning that if there is no follow-on child element, don’t render the previously mentioned vertical line since it would extend downward to the bottom of the root <ul>
element’s height.
In the next post, I show you how much simpler this is with Vue.js. ☮