Correct use of axes is at the core for creating user-friendly data visualisations. Axes are important for all sorts of graphs, be it bar charts, histograms, scatter plots, time series, bullet charts and many more. They provide a reference for the graphics so that the user can extract meaningful information about the data. A good axis is subtle and non-intrusive, yet stand by to provide valuable meaning whenever the user requires it.
Let’s dig into a few tips and tricks that I’ve learned over the years.
1. Display ticks as percentages
Whenever you’re visualizing data that are percentages, the axis should obviously reflect this. The raw data, however, might be (or perhaps should be) a decimal number (i.e. 0.25 instead of 25%).
We want to turn this:
into this:
Here’s the code to achieve this:
As you can see, it is d3.format()
that does the number formatting here. axis.tickFormat() lets you format your axis’s ticks in whatever way you’d like, and in this case we want to convert whatever the initial numeric value is into a percentage.
d3.format() has a variety of formatting options, and the value “~%” means the following:
- the
%
(percentage sign) multiplies each value with 100 and adds the percentage sign, and - the
~
(tilde) removes all insignificant trailing zeros
Without the ~
we’d end up with numbers like “20.0000%”, but luckily d3 has a way to handle that.
2. Dynamic number of ticks
There are several challenges when creating responsive visualisations. It can be difficult to make a graph look good on both small and large screens, and sometimes the x-axis can cause some headache. Out of the box our d3-axes will just guess the appropriate number of ticks based on its scale.
Here’s what I’m talking about.
D3 does some calculations under the hood, trying to optimise the number of ticks, but it doesn’t always get it right. And especially when you want the number of ticks to react to a changing width, you need the extra bit of control.
To do this, we can use .ticks()
.
This code renders a cleaner axis:
On line 4 above we call .ticks(3)
on the axis. Note that the actual number of ticks rendered on the axis may differ from the number passed into the function though. D3 will still try and optimize the number of ticks, but most of the time the tick count it will be a maximum of one off.
By dynamically set the number of ticks, we can easily get an axis that responds to the available width.
We take the width of the graph and divide by the width we want in between each tick.
All what’s left is to update/call the axis each time the width changes.
3. Create grid lines with .tickSize
Sometimes (but maybe not as often as you might think) grid lines can enhance a graph.
As a rule of thumb, grid lines should be an extension of a labelled value on the axis guiding the readers’ eye to the a quantitative value.
The easiest way to add grid lines using d3 is to tweak an axis.
We start by setting up our axes and scales:
Now we have something like this:
All d3-axes have a .tickSize()
method that controls the length of each tick mark. By setting this to a negative value we can actually get it to cross over the perpendicular domain line and turn into grid lines.
And then we can reduce the opacity of the grid lines after we’ve called the yAxis …
… and add some random data:
4. Animate axis on update
Clever use of animation can sometimes elevate the visualisation and its message. Transitioning the axis from one state to another is as easy to do in d3 as transitioning any other element (i.e. bars, lines), and is equally important. A good use for transitioning or animating a graph is to help the user quickly understand the direction and distance the data is changing.
Bar chart races like this are good examples of animated axis. Here they give the audience clues for the speed in which the data change.
Now imagine we have a simple time series chart displaying sales of Product A of a fictional company. And that this visualisation takes part of a bigger page with details about this particular product.
If a user then navigates to a different product, Product B, then the graph should not just refresh instantaniously, but instead transition in accordance to the other product’s data.
Transitioning an axis is as simple as setting group.transition() before calling the axis.
5. Custom axis styling
Out of the box, d3-axes have okay styling, but if you want to adjust their appearence, there are a few things that are neat to be aware of:
Unless you explicitly set styles like font-size
and font-family
to the svg’s <text>
elements, they will render at 10px size and your device’s default ‘sans-serif’ font.
Depending on your preference, this can be solved with d3 or CSS.
For CSS, we can utilise that D3 adds class ‘tick’ to each of the ticks on the axis.
When changing the font of the axis, it might be an idea to consider using a font that either has tabular figures build-in, or supports the font-variant-numeric: tabular-nums;
CSS property to ensure that numbers are aligned correctly.
If you want a slightly cleaner look for you axis, you might consider hiding or reducing the visual prominence of the axis’s domain. The domain is the <path>
element that D3 injects that runs along the axis, typically separating the ticks from the graph. Styling the domain can also be done either directly in the JavaScript or with CSS.
For JavaScript, you need to select the axis group’s path.domain
child and add styles or attributes to it. So expanding on our example above we can do the following:
This reduces the opacity of the domain path to 0.25. Here, you could instead set its style to hidden
to hide it or call the .remove()
method on it to remove it from the DOM completely.
6. Include unit of measurement on the Y‑axis
Labelling all axes is important. Not only must the reader easily understand what the values on the axis refer to, but also when they export or otherwise use the graph outside its intended context, the unit of measurement should be easily accessible.
One trick for achieving this is to include the y-axis’s unit of measurement directly on the axis itself.
By extending the first tick’s label to include what’s measured can be useful if the chart’s design allows it.
In the d3 script we can create and call a function addUnit()
for the y-axis, and pass the unit of measurement as a parameter. On line 2
we import our helper function which we call on line 9
.
In our helpers file we export the following function.
What happens here is that we find the last (in DOM) tick for the axis (line 2
) for which we add a <rect>
(a white background masking the tick mark and domain line below the text) and a <text>
element. We use the length of the text element to set the width of the rectangle.
Please only use this snippet as a guide for how to achieve this effect.