Overlapping Bar Charts
As the name suggests, overlapping charts visualize two different sets of data in a single diagram. The idea is that the overlapping bars allow us to compare data, say, year-over-year. They are also useful for things like tracking progress for a goal where one bar represents the goal and the other shows the current amount.
But they’re beautiful too!
Your mind is probably like mine and is already starting to figure out how you’d go off and code that. Here’s how I tackled it.
The HTML
We’re going to start with markup because, well, that’s how we know what needs styling.
<div class="container">
<div class="chart">
<dl class="numbers">
<dd><span>100%</span></dd>
<!-- all the way to 0% -->
</dl>
<dl class="bars">
<div>
<dt>2018</dt>
<dd>
<div class="bar" data-percentage="50"></div>
<div class="bar overlap" data-percentage="53"></div>
</dd>
</div>
<div>
<!-- more bars -->
</dl>
</div>
</div>
We will be using description lists (
- ) as it is a much more semantic approach as compared to standard ordered and unordered lists. Another reason is that we are including a label within each bar. Normal lists do not have a tag within them to add a title or description unlike definition lists. In simple terms, it just makes more sense and is more readable too.
The first description list, .numbers
, is the y-axis. The .bars
is where the data is visualized and I’ve made a definition list to build the x-axis as well. Each list item contains a .bar
and the label as a description term (dt).
And what’s up with the data attribute? The data-percentage
is being used to specify the height of the bar, which ultimately represents its value on the y-axis. We could manually set it in CSS for each bar, but that is repetitive and a lot of extra code that can be replaced with a few lines of CSS.
The basic chart styles
We’re working with a lot of two-dimensional directions, so flexbox is going to be our friend for getting everything lined up. We can make the .chart
element a flexible container that positions the y-axis labels and the chart beside one another in the row
direction.
.chart {
display: flex;
}
We don’t even need to specify the direction since flexbox defaults to row
. Let’s do that and then add flexbox to the list of labels along the y-axis while we’re at it since we know those will run in the column
direction.
.numbers {
display: flex;
flex-direction: column;
list-style: none;
margin: 0 15px 0 0;
padding: 0;
}
Believe it or not, we can use flexbox again for the bars since, they too, are running in a row
direction.
.bars {
display: flex;
flex: auto; /* fill up the rest of the `.chart` space */
gap: 60px;
}
I’ve set this up so that the .bars
automatically take up whatever space is leftover by the y-axis
.numbers
.
You probably noticed it in the HTML, but “bar” is actually two bars where one overlaps the other. I wrapped those in a generic
) we’re using as a label and the description details (
) that holds both bar values:
.bars > div {
align-items: center;
display: flex;
flex-direction: column;
flex: 1;
position: relative;
}
Each bar is going to be the same width, hence flex: 1
. We’re relatively positioning the element while we’re at it because we’re about to absolutely position each bar and we want to make sure they stay in their containers.
Each bar has a percentage height that corresponds to the values along the vertical y-axis. You may also remember that we gave each bar a data-percentage
attribute — we’re going to sprinkle in a little JavaScript that sets the height of each bar using those values.
var bars = document.querySelectorAll("dd .bar");
bars.forEach((bar) => {
var height = bar.getAttribute("data-percentage");
bar.style.height = height + "%";
});
That’s our basic chart!
We want to get this to where we can see the bars overlapping one another. That’s next!
Overlapping bars
The trick to get one bar to overlap another is funny because we’re often trying to prevent things from overlapping visually in CSS. But in this case, we actually want that to happen.
The bars are already overlapping; it’s just tough to tell. Notice in the HTML that the second .bar
in each set has an additional .overlap
class. Let’s use that to differentiate the bars. You’re totally free to choose your own styling for this. I’m adding a little padding to the .overlap
bars so that they are wider than the other bars. Then I’m tweaking the stacking order using z-index
so that the .overlap
bars sit below the other bars.
Let’s add a legend
Legend. Such a great word, isn’t it? Packed with all kinds of meaning. In this case, it’s a more than a nice touch because, visually, we’re jamming two bars in spaces that are typically reserved for one bar. A legend provides context that explains what each bar represents.
<figure class="legend">
<div class="type1">Estimate</div>
<div class="type2">Actual</div>
</figure>
Using a