We want others to be able to share this work, so we have created a reusable, Javascript-based component that you could use for your data. This tutorial walks you through how it was created. You can get the final source code for this component on GitHub. Most of the code is original, but the magic formula to calculate font sizes based on a string’s content came from the NYT original article.

Breaking Down The Visual

Before starting any code, we need to know what our desired markup end state is. You can also see a live preview of this on CodePen.

Markup

Looking at the HTML, it appears that every entry makes up a row. The rows are sorted first alphabetically by a high-level category (i.e. “Agriculture”, “Commerce” , Education”…) Then they are sorted based on the size of the value within that higher level category. So this visualization is encoding two pieces of information:

The absolute weight of each value to the absolute set (encoded by font size).

The relative weight of each value within its category (encoded by sort order).

We can think of the high-level category as the Level 1 grouping of the data (e.g. “Agriculture”) and the lowest-level detail associated to the measure as Level 2. Notice that the list also the Total for that Level 2 line item. The value shows the per capita cost, which is roughly the total spend divided by 318.9 million, or the estimated US population in 2014.

Each row is placed in a block div with an o-row class. That row is divided into two spans. The number, which we want to vary in size, is classed o-measure. The font is sized explictly with an element level attribute, to override all CSS. The detail that appears to its right in three lines gets a o-detail tag.

Javascript

Now let’s look at the code that dynamically reads the data and generates the markup.

Reading the JSON data

Conveniently, our data is already in a JSON file like this. We can start to identify that Its font size should be scaled proportionately to its relative value among all the values. We also want to find the subtotal of a particular category.

The callback we provide is to the processData function. This function is really the “main” function for our script. Calling this function directly allows us to bypass reading the JSON data from an external source.

Structuring the data

First, we want to shape the data to remove unnecessary elements, and send it along to the rendering functions in a predictable format. This function accepts the original data and a single object called “options” which contains the names of the properties to be read as level 1 (L1), level 2 (L2) and the measure.

We use Javascript’s native Array.map function to create a new array by calling a function on every element of our original array. The function checks for the existence of such a property on every element. If found, it sets strings to lower case and converts numbers to float.

Finding subtotals

Originally, I thought that the “total” field contained the subtotal of Level 1. During the writing of this post, I realized that it really shows the total of Level 2. Still, showing a subtotal of the L1 category is a useful feature to add, so it’s included below. This should probably be made an option for the viz, whether to simply show a different value as the total, or subtotal L1.

Rendering the Output

Now that the data is shaped, sorted, and subtotaled, we are ready to write out the desired HTML. This is accomplished by using jQuery to append elements to some given root element. We use the toLocaleString function to add the culture-aware separators and decimal markers to numbers.

Scaling the fonts

At this point, the markup is basically what we want. Everything is perfect – except the fonts aren’t scaled. Here we come to the magic formula. You can find the original code for this embedded in the markup of the inspiration article. I borrowed heavily from it, though I did clean it up a bit.

It uses a scaler value to become a common multiplication factor based on the window’s actual width, within a given maximum and minimum window size.

You reach the font sizing function (line 35), which seems to plumb the depths of typographical sorcery. It divides the value of the number you want to scale by a ratio how many numeric and non-numeric characters it contains. Then we take the square root of that figure. (See note on why we need the square root here.)

Responsive design

We want the font scaling to look right on many sizes of screen. More than that, we want it to adjust if the screen size is changed. To support this responsiveness, we can tie into jQuery’s $(window).resize function and scale the fonts every time the window is resized.

Pay special attention to the $.debounce() function. Debouncing is a technique that delays a call to an event handler if the event is raised many times in a given span of time. Without debouncing, our resize will be called for literally every window resize event. This event may be raised hundreds or thousands of times as a user drags and drops a corner. Debouncing raises the event only once, after the specified 250ms delay, so font resizing only happens when the window settles down.

Next steps

Node and Jade

The initial effort was to make a more reusable script. But there are many ways to improve. In Part 2 of this series, I’ll rearrange the code to add options for users, and make the root element more configurable.

To make the code much more reusable, I’ll also wrap it as a node package that can be easily incorporated into other node modules with a simple require statement. This will become more important to integrate this into the OpenBudgetOKC source, which is based on node and also uses Jade for HTML templating.

When that’s ready to go, I’ll open a pull request (a new experience for me).

I hope this code and walkthrough can help you to create your visualization!

Taking the square root with fonts

Taking the square root is important when scaling fonts. Comparing the sizes of fonts is like comparing the areas of two circles. The font-size setting is like controlling the areas of these circles with the radius. Say we want to compare two values: 1 and 2. If we simply set the radius of two circles to 1 and 2, we actually produce circles with these areas (remember ):

Area with radius 1:

Area with radius 2:

In trying to show that 2 is twice as large as 1, we will inadvertently create a circle that is actually 4 times larger. Taking the square root addresses this issue: