I have a system that generates a list of nodes which may or may not have additional elements between the nodes. I want to visualize this list horizontally with lines connecting the nodes as shown in the example below.
The content between two nodes is variable and can even contain more complex elements like images and tables. The nodes themselves are labeled with only text (Point A, Point B, etc.).
In my first attempt at trying to visualize the nodes I simply used a vertical list with the :before
and :after
CSS pseudo-elements to draw the lines between the nodes. I am however having difficulty translating this approach to a horizontal list.
Horizontal approach: (updated thanks to Mayank Gupta's answer below)
h1, h2 {
margin: 0;
padding: 0;
}
ul {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
flex-direction: row;
text-align: center;
list-style-type: none;
font-size: 1.5em;
}
ul li {
display: flex;
justify-content: flex-start;
flex: 2;
position: relative;
border-bottom: 2px solid #000;
padding-bottom: 10px;
}
ul li:after {
position: absolute;
bottom: -5px;
left: 0;
font-family: "Font Awesome 5 Free";
font-weight: 300;
content: "\f111";
height: 17px;
width: 23px;
background: #fff;
}
ul h2 {
margin: 0 0 0 -1em;
padding: 0;
}
ul li:last-of-type {
border: none;
}
ul li > div {
flex: 0 0 auto;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
<link href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" rel="stylesheet"/>
<ul>
<li>
<h2>Node A</h2>
<div>Content</div>
</li>
<li>
<h2>Node B</h2>
<div>Content</div>
</li>
<li>
<h2>Node C</h2>
</li>
</ul>
Problems with this method:
How could I resolve these issues?
Here is my answer - essentially you add display:flex
to the ul
list and make sure that the nodes have fixed size by using flex:0 0 auto
and a fixed width. This would allow for the context between to be variable in size.
Concerning the connection of the nodes, although it could be done by using the :before
and :after
pseudo-elements, there's an easier way to do it; just use a pseudo-element for the whole list (the ul
element) that goes side-by-side along the width of the element (in the example actually left
and right
have a value of 30px to compensate the arbitrary side padding I set).
HTML:
<ul>
<li class="node"><label>Point A</label></li>
<li class="context"><span>Content here Content here Content here</span></li>
<li class="node"><label>Point B</label></li>
<li class="context"><span>Content here</span></li>
<li class="node"><label>Point C</label></li>
</ul>
CSS:
ul {
list-style-type: none;
display: flex;
width: 100%;
margin: 50px 0 0 0;
padding: 0 30px;
position: relative;
}
ul:before {
content: "";
display: block;
position: absolute;
left: 30px;
right: 30px;
top: 50%;
margin-top: -1px;
height: 2px;
background: steelblue;
}
.node {
flex: 0 0 auto;
display: block;
width: 12px;
height: 12px;
border-radius: 12px;
border: 2px solid steelblue;
position: relative;
background: #fff;
}
.node label {
position: absolute;
top: -30px;
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
}
.context {
flex: 1 1 auto;
}
.context span {
display: block;
margin-top: -15px;
text-align: center;
}
Here is a working fiddle: https://jsfiddle.net/8ksxtz60/