How to Make Line Charts in Pure CSS

Plenty of time in the past, I’ve shown you how to create different types of charts using CSS and JavaScript. Today, I’ll guide you on how to build line charts in pure CSS.

What is a Line Chart?

A line chart looks like a cardiogram; it consists of data points that are connected through a line. 

Line chart showing the population of the town of Pushkin, Saint Petersburg from 1800 to 2010, measured at various intervalsLine chart showing the population of the town of Pushkin, Saint Petersburg from 1800 to 2010, measured at various intervalsLine chart showing the population of the town of Pushkin, Saint Petersburg from 1800 to 2010, measured at various intervals
Line chart showing the population of the town of Pushkin, Saint Petersburg from 1800 to 2010, measured at various intervals – Taken from Wikipedia

This kind of chart can provide useful information for trends over the year, price changes, compare behaviors, etc.  

1. Begin With the Data

For this demo, we’ll assume that we want to visualize in a line chart the following data that describe the employee count over the years:









Year Number of Employees  
2010 40  
2013 30  
2016 25  
2019 35  
2022 40  

2. Specify the Page Markup

We’ll specify a wrapper element that contains two lists:

  • The first list sets the y-axis range. If you look at the table data above, you’ll see that the second column includes values up to 40. Keeping this in mind, we’ll define six values from 0 to 50 with a step size of 10. The values of the y-axis will therefore be 0, 10, 20, 30, 40, and 50.
  • The second list sets the x-axis data. These are extracted from the first column of our table, from lowest to highest. Consider also the start, end, and delay CSS variables that we pass to the list items. We’ll use them to create the data points that will construct the line.

Here’s the required markup:

1
  <div class="chart-wrapper">
2
    <ul class="chart-y">
3
      <li>50</li>
4
      <li>40</li>
5
      <li>30</li>
6
      <li>20</li>
7
      <li>10</li>
8
      <li>0</li>
9
    </ul>
10
    <ul class="chart-x">
11
      <li style="--start:80%; --end:60%">
12
        <span>2010</span>
13
      </li>
14
      <li style="--start:60%; --end:50%; --delay: 1">
15
        <span>2013</span>
16
      </li>
17
      <li style="--start:50%; --end:70%; --delay: 2">
18
        <span>2016</span>
19
      </li>
20
      <li style="--start:70%; --end:80%; --delay: 3">
21
        <span>2019</span>
22
      </li>
23
      <li style="--start:80%; --end:80%; --delay: 4">
24
        <span>2022</span>
25
      </li>
26
    </ul>
27
  </div>

Let’s now explain the values of these variables:

  • The start values will describe the number of employees per year. The highest value in the y-axis is 50, which means 100% in percent mode. So, for representing 40 employees, we’ll use this calculation: (40 / 50) * 100 =>80%. Similarly, for 30 employees, we’ll use this: (30 / 50) * 100 =>60%, etc.
  • To connect the data points, the end value of all list items apart from the last one should match the start value of their next sibling list item. That said, the end value of the first list item will match the start value of the second list item, the end value of the second list item will match the start value of the third list item, etc.
  • The delay values will set the chart animation sequence. The higher the value, the more time the related animation needs to run.

3. Style the Chart

For simplicity, we’ll skip the reset/basic styles. You can check them by clicking the CSS tab of the demo project.

The chart wrapper will be a flex container with a 20px gap between the axes. The y-axis width will be auto while the x-axis will expand to cover the remaining space. 

The chart axesThe chart axesThe chart axes

The x-axis and its items will also be flex containers. The items will equally be distributed across the main axis, and their content will sit at the bottom.

Here are the relevant styles:

1
.chart-wrapper {
2
  display: flex;
3
  gap: 20px;
4
}
5

6
.chart-wrapper .chart-x {
7
  display: flex;
8
  flex-grow: 1;
9
}
10

11
.chart-wrapper .chart-x li {
12
  display: flex;
13
  align-items: flex-end;
14
  flex: 1;
15
}

Next, we’ll define the ::before and ::after pseudo-elements of the list items.

The ::before pseudo-elements will construct the lines, while the ::after pseudo-elements, which will be optional, will create the data points.

The chart styling using CSS pseudo-elementsThe chart styling using CSS pseudo-elementsThe chart styling using CSS pseudo-elements

Line Animation

To create the line animation, like we’ve done in many tutorials in the past, we’ll take advantage of the clip-path property and specifically its polygon() CSS function.

Before moving further, it’s worth noting that the easiest way to understand the process we’re going to follow is by using a CSS clip-path maker like Clippy.

Just as a reminder, an element with clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%) makes it appear entirely.

In our case, let’s concentrate on the line animation for the first list item that has a start value of 80% and an end value of 40%.

1
<!--FIRST LIST ITEM-->
2
<li style="--start:80%; --end:60%">
3
  <span>2010</span>
4
</li>

Initially, we’ll set its clip-path property to polygon(0 20%, 0 20%, 0 20%, 0 20%) which will make all points sit at the same position.

Initial clip-path property for the first list itemInitial clip-path property for the first list itemInitial clip-path property for the first list item

When the page loads, we’ll update the clip-path value to polygon(0 20%, 100% 40%, 100% calc(40% + 2px), 0 calc(20% + 2px)). That way, the position of the last three points changes, and a line is created whose thickness we can control.

End clip-path property for the first list item after page loadsEnd clip-path property for the first list item after page loadsEnd clip-path property for the first list item after page loads

Please note that in the Clippy tool, we cannot add CSS. That’s why I tried 41% and 21% instead of calc(40% + 2px) and calc(20% + 2px) that appear in the styles.

With all this in mind, we can now construct dynamic styles regardless of the chart’s data points.

Here are the related styles:

1
/*CUSTOM VARIABLES HERE*/
2

3
.chart-wrapper .chart-x li {
4
  position: relative;
5
}
6

7
.chart-wrapper .chart-x li::before,
8
.chart-wrapper .chart-x li::after {
9
  content: "";
10
  position: absolute;
11
  left: 0;
12
  background: var(--chart-color);
13
}
14

15
.chart-wrapper .chart-x li::before {
16
  top: 0;
17
  width: 100%;
18
  height: 100%;
19
  clip-path: polygon(
20
    0 calc(100% - var(--start)),
21
    0 calc(100% - var(--start)),
22
    0 calc(100% - var(--start)),
23
    0 calc(100% - var(--start))
24
  );
25
  transition: clip-path 0.5s calc(var(--delay, 0) * 0.5s);
26
}
27

28
.chart-wrapper .chart-x li::after {
29
  top: calc(100% - var(--start));
30
  width: 10px;
31
  height: 10px;
32
  border-radius: 50%;
33
  transform: translateY(-50%);
34
}
35

36
.loaded .chart-wrapper .chart-x li::before {
37
  clip-path: polygon(
38
    0 calc(100% - var(--start)),
39
    100% calc(100% - var(--end)),
40
    100% calc(calc(100% - var(--end)) + var(--chart-thickness)),
41
    0 calc(calc(100% - var(--start)) + var(--chart-thickness))
42
  );
43
}

Lastly, it’s worth noting that to ensure that the chart animation will run on page load, we’ll use JavaScript to add the loaded class to the body element.

1
window.addEventListener("load", function() {
2
  document.body.classList.add("loaded");
3
})

Don’t forget to use your browser tools to test the resulting clip-path value for each item.

Multi-Line Charts

With our single-line chart in place, we can go even further and create CSS-only multi-line graphs with just a little more extra markup. 

Consider the following demo which follows the same principles we discussed in the single-line graph:

Conclusion

We just completed another CSS chart tutorial, folks! I hope you enjoyed the demos we developed and learned one or two new things.

Of course, you can always create something more advanced using a JavaScript charting library. But, on the other hand, it’s always fun to go the hard way by experimenting with modern CSS techniques.

As always, thanks for reading!