Skip to main content

About

Making Data Viz Without SVG Using D3 & Flexbox

Bye bye tricky alignments.

July 2018

A few months ago, my co-worker Matt and I were collaborating on a project. I had been building a stacked bar chart in D3.js, and was seriously struggling to get the bars to stack on top of each other nicely and to animate from the bottom of the graphic instead of the top. For some reason, the fact that an SVG space places its (0,0) coordinates at the top left corner instead of the bottom left corner will always cause some weird mental disconnect in my brain.

Amid all of my frustration, Matt suggested ditching the SVG altogether, making the bars out of div elements with a background-color and aligning them using Flexbox. To be totally honest, I was super confused by the suggestion because it involved a few things that I honestly didn't know were possible. I should have asked: You can make D3 graphics without SVG? Divs can be used to make bars? I can use flexbox to do all the alignment math for me?

It turns out the answer to all of those questions is yes. In that moment, I was being stubborn and eventually got the SVG to work the way I wanted, but Matt’s suggestion stuck with me. And when I actually saw it in action in one of his more recent projects, I was mind-blown. I ended up loving it so much that one of the most recent data-viz stories that I programmed contains no SVG. I’m so in love with this method (for very specific circumstances) that I wanted to shout it from the rooftops...or at least from this post.

In this post, I’ll give some background information that I wish I knew, and an example of how to use this method to make a waffle chart, a modified histogram, and a stacked bar chart.

Some Important Context

If you’re already super-familiar with the inner-workings of D3.js, DOM elements, and Flexbox, you can skip this section.

Although I’d been using D3 to make graphics for over a year, I had some major misconceptions about what it can do that stopped me from fully grasping this concept early on. So here’s some things I wish I knew:

Making a Waffle Chart

Let’s start with a waffle chart. In this kind of graphic, we’ll use small blocks to represent a percentage of something (much like a square pie chart). For this example, we’ll say that 5% of our readers resize their browser.

To make this kind of graphic, we’ll need a single figure element with 100 smaller div elements inside of it. Like this:

HTML

In your HTML file, you’ll only need a single, empty figure element. Remember, no need to add an SVG element.


		
		  

JavaScript

Since we’re going to need 100 blocks (in our case, div elements) inside of our figure element, we can add them with JavaScript. In this example, we’re creating an array of 100 numbers and creating a div for each one. To make our graphic represent 5% of something, we need 5 of our 100 blocks to be stylistically different from the rest. Here we set the style background-color to be red if the number is less than 5 and gray if it’s greater than 5.


		
		  

CSS

The magic of this method shows up here, in the CSS. At this point, we have an outer-most figure element called waffle and 100 divs inside that are each called block. Using CSS, we can define the size of our block and waffle elements, and use Flexbox to arrange them. Be aware that in this example, we aren’t including any vendor prefixes. You can automatically add them by running this CSS code through an autoprefixer.


		  

Here, the designation display: flex indicates that the waffle figure element should be considered a flex container. The line flex-wrap: wrap indicates that the items inside of waffle should line themselves up in a row (starting in the upper left-hand corner) until they reach the end of the container (in this case, width: 140px), and then form a new row. If you want this process to start in the lower right-hand corner instead, use flex-wrap: wrap-reverse. Last, give your block elements a width and height to make them visible. That’s it!

The Results

Here’s what our final graphic looks like. Feel free to inspect it to double-check that it’s only made up of div elements.

Making a Modified Histogram

Histograms are generally used to show a distribution in the data. While a typical histogram uses a single bar of varying height to signify the count of items in a bin, at The Pudding, we often use a type of modified histogram, where small shapes of identical size are stacked on top of one another. So, instead of a 100px tall rectangle representing a count of 10, you could have 10, 10px tall rectangles (spaced 1px apart) representing a count of 10.

To make a modified histogram using this method, you need to plan for the overall structure of your graphic. You’ll need a figure element to contain the entire graphic, a div for each stack of blocks, and a div for each block. Like this:

HTML

Again, we’ll start with a single figure element to contain everything.


		
		  

JavaScript

For this example, we’ll make a histogram of the state where each of The Pudding team members lives. We’ll manually add the state data for each of our 6 members, but you could have imported the data using D3.csv or D3.json. Next, we need to nest the data by state, add a div to contain all the blocks we’ll add for each state. Then, to each group div we need to add the appropriate number of block div elements. Last, we’ll add a state label to each group.


		
		  

CSS

Here we have two elements that need to act as flexbox containers. To align the block div elements and the label in a column, we assign the group div display: flex and flex-direction: column. Then, we want all 3 groups to sit next to one another in a row, so we assign the hist figure element display: flex and flex-direction: row. This automatically aligns the groups in a row, but they are also aligned at the tops of their groups (so the stacks look like they are hanging from the top of the graphic instead of growing from the bottom). To fix this, we assign align-items: flex-end.


		
		  

The Results

Here’s what our final graphic looks like. Once again, feel free to inspect it to double-check that it’s only made up of div elements.

Making a Stacked Bar Chart

Stacked bar charts are only slightly more complex than waffle charts or histograms because they require 2 additional elements: different sized block div elements and a way to quantify the overall height of the bars. Structurally, it needs to be set up like this:

In this example, we’ll look at a small part of our site’s traffic. Specifically, the traffic that has come from Facebook, Twitter, YouTube, and Instagram over the past few months.

HTML

Just like our first two examples, you start with a single figure element.


		
		  

JavaScript

Our process of preparing our elements in JavaScript is similar to how you would write the JavaScript code for an SVG-rendered stacked bar chart, with one-notable example. You don’t need d3.stack(). That specific function calculates the bottom and top positions of each rectangle in your stack, and since we’ll use Flexbox to arrange our shapes, that becomes unnecessary. Instead, we need to append a div element for each month, and then append additional div elements for each social media type. Last, we have to set a scale (here we use d3’s built in d3.scaleLinear()) and use the scale to define the height of our div elements (.style(‘height’, function(d){return `${scaleY(d.value)}px`)). Instead of a full y-axis, we’re going to just add a value label to sit on the top of our bars.


		
		  

CSS

The styling for this stacked bar chart is very similar to our modified histogram in that we want our block elements to be aligned in a column and our group elements to be aligned in a row. I’ve added a few other pieces of styling to arrange the items in the way that I want. Originally, the bars for were at the top of the stack because they are the first item in my array for each month. Since I want to draw my reader’s attention to these bars, I want them to have the same baseline, and so I want them to be on the bottom. Using Flexbox, I can reverse the order of my bars using flex-direction: column-reverse.

Then, I want the month label to be at the bottom of my bars, so I can assign it an order: 0 (if our bars were still organized using flex-direction: column in the default order, order: 0 would put this label at the top of the stack). And I want the number count label to be at the top of the bars, so I assign it an order: 2. My block elements are assigned an order: 1 to keep them between both labels.


		
		  

The Results

Here’s our final image (with a little bit of extra styling).

That's it!

Basically, this method takes away the need to calculate the exact positioning of all of our blocks and gives that responsibility to Flexbox to handle automatically. It works especially well when all of your elements can line up in a grid of rows and columns. If you’re interested in other ways to use D3 without SVG, check out this oldie but a goodie post from Jim Vallandingham about how to make an HTML bubble chart or this simple example from the D3 documentation that I totally missed 🤦🏻‍♀️.

This method doesn’t work as well if you have elements that would need to reach across your rows or columns. For instance, you can add axes (check out some examples of that here) but if you want gridlines to extend from your axes all the way behind your graphic...this method won’t work for you. Also, if you want annotations that aren’t going to line up in a grid...stick with SVG. Boxes arranged in this way also don’t smoothly animate from one position to another. So, you’ll lose object constancy if your graphic includes any animation.

But otherwise, give it a try! And hit us up with any questions at amber@pudding.cool.