Creating a Bar Graph with CSS Grid

Avatar of Preethi
Preethi on

If you’re looking for more manageable ways to create bar graphs, or in search of use cases to practice CSS Grid layout, I got you!

Before we begin working on the graph, I want to talk about coding the bars, when Grid is a good approach for graphs, and we’ll also cover some code choices you might consider before getting started.

Preface

The bar is a pretty basic shape: you can control its dimensions with CSS width, height, number of grid or table cells, etc. depending on how you’ve coded it. As far as graphs go, the main thing we want to control is the height of the bars in the graph.

Controlling height with Grid cells (like here) is convenient for designs where the height is incremental by a fixed value — no in-betweens. For example, signal bars in phones or when you don’t mind setting a lot of grid rows to better control the bar height down to its smallest value, like IRL graph paper.

Five vertical purple bars that get progressively taller from left to right like a signal indicator on a cell phone.

For my graph, I want gradient bars as well as vertical and horizontal axes labels. So, to make it easy, I have decided to control the bar height with gradient sizing, and determine the number of grid rows based on the number of vertical axis labels I want.

Also, other than the contents for the graph — bars, axes labels, and captions — there’ll be no data present in the HTML, like data about bar colors and dimensions.

data-* attributes are used to provide that sort of information in HTML. But I didn’t want to switch back and forth between HTML and CSS while coding, and decided to completely separate the content from the design. It’s totally up to you. If you feel like using data-* might benefit your project, go for it.

I’ve created a diagram below that you might find useful to refer to while reading the code. It depicts the graph and the grid that contains it. The numbers represent grid lines.

Four gold cylindrical bars on a graph where the y-axis goes from 0 to 100 in increments of 10 and the x-axis is labeled with different smiley face emoji.

Let’s code this thing.

The HTML

Grid can automatically place items in top-bottom and left-right directions. To take advantage of that, I’m going to add the graph contents in the order y-axis labels (top-bottom), bars, and x-axis labels (left-right). This way, I only need to write the HTML markup and the CSS will place the bars for me!

<figure aria-hidden="true">
    <div class="graph">
        <span class="graphRowLabel">100</span>
        <span class="graphRowLabel">90</span>
        <span class="graphRowLabel">80</span>
        <span class="graphRowLabel">70</span>
        <span class="graphRowLabel">60</span>
        <span class="graphRowLabel">50</span>
        <span class="graphRowLabel">40</span>
        <span class="graphRowLabel">30</span>
        <span class="graphRowLabel">20</span>
        <span class="graphRowLabel">10</span>
        <div  class="graphBar"></div>
        <div  class="graphBar"></div>
        <div  class="graphBar"></div>
        <div  class="graphBar"></div>
        <div  class="graphBar"></div>
        <span><sup>Y </sup>&frasl;<sub> X</sub></span>
        <span class="graphColumnLabel">&#x1f60a;</span>
        <span class="graphColumnLabel">&#x1f604;</span>
        <span class="graphColumnLabel">&#x263a;&#xfe0f;</span>
        <span class="graphColumnLabel">&#x1f601;</span>
        <span class="graphColumnLabel">&#x1f600;</span>
    </div>
    <figcaption>Made with CSS Grid &#x1f49b;</figcaption>
</figure>

<span class="screenreader-text">Smiling face with squinting eyes: 10%, grinning face with squinting eyes: 65%, smiling face: 52%, grinning face with smiling eyes: 100%, and grinning face: 92%.</span>

Note: If you’re interested in accessibility, know that I’m not an accessibility expert. But when I tried to make the bars accessible, screen reader experience simply sucked. Using aria-labelledby wasn’t that good either. So, I added a text description of the graph and hid it from the visual display. That made the reading much more natural.

The CSS

This is where the magic happens.

/* The grid container */
.graph {
  display: grid;
  grid:  repeat(10, auto) max-content / max-content repeat(5, auto);
  /* ... */
}

We’ve defined eleven rows and six columns in our grid with these two little lines of CSS: ten automatically sized rows and one sized to its “maximum content”; one column sized to its “maximum content” and five automatically sized. CSS Grid is a beautiful thing.

The graph bars need to cover the grid from the first row to the second-to-last row since we are using the last one for the x-axis labels. I gave the bars 100% height, and grid-row: 1 / -2; which means “span the items from first horizontal grid line till the second last.”

/* A grid item */
.graphBar{
  height: 100%;
  grid-row: 1 / -2;
}

The bars also have linear gradient going upwards. The size of the colored portion of the gradient is the indicator of bar’s height, which in turn is taken from each bar’s own CSS rule as a custom variable.

/* A grid item */
.graphBar{
  /* Same as before */
  background: palegoldenrod linear-gradient(to top, gold var(--h), transparent var(--h));
}

To control the width of the bars and the space between them, I use a fixed width and centered them with justify-self: center;. You can instead use grid-column-gap to create gaps between columns if you want. Here’ the full code that pulls everything for the bars together:

/* A grid item */
.graphBar{
  height: 100%;
  grid-row: 1 / -2;
  background: palegoldenrod linear-gradient(to top, gold var(--h), transparent var(--h));
  width: 45px;
  justify-self: center;
}

Did you notice the CSS variable (var(--h)) in there? We need to specify the exact height of each bar and we can use the variable to determine the height of the background gradient in terms of percentage:

.graphBar:nth-of-type(1) {
  grid-column: 2;
  --h: 10%;
}
.graphBar:nth-of-type(2) {
  grid-column: 3;
  --h: 65%;
}
.graphBar:nth-of-type(3) {
  grid-column: 4;
  --h: 52%;
}
/* and so on... */

That’s it! after styling, the graph looks like this:

See the Pen CSS Bar Graph with Grid by Preethi (@rpsthecoder) on CodePen.

There are a few demo-specific styles in here but everything we’ve covered so far will get you the basic framework for a bar graph. The y-axis labels I created are positioned on top of the grid lines for a slightly cleaner layout. I got the cylindrical shape and the cross-section edges of the bars by using border-radius and elliptic pseudo elements, respectively. Without them, you’ll get a straight up rectangular bar.