This blog looks at how CSS flexbox layout can be applied to SVG in order to simplify the task of constructing charts with D3. This approach has been made possible by the JavaScript flexbox implementation that Facebook recently open sourced to support ReactJS Native.
Here’s a quick example, where the layout of a chart is defined using SVG and flexbox via the layout-css
attributes:
And here’s a chart that makes use of the above layout:
Introduction
If you’ve ever wanted to plot a chart or create a visualisation, you will no doubt have come across D3. The power and versatility of this framework has resulted in it becoming one of the most popular repositories on GitHub.
D3 excels at transforming data into SVG or HTML elements, allowing charts to be constructed with very little code. However, D3 does little to help with the more mundane task of layout; the positioning of axes, labels, the legend etc …
Mike Bostock (D3’s creator) has published a simple Margin Convention which he uses in his own examples. As you can see from the code below the simple task of applying a margin around the chart requires some fiddly maths:
Once you start adding axes, titles or a legend things really start to get out of hand …
To be fair, this isn’t really a fault of D3, the problem of layout simply isn’t within the remit of this library.
With SVG elements are positioned using a simple coordinate system, which also doesn’t help us much when trying to construct a suitable layout.
Whilst battling with this problem, my colleague Chris Price came up with a great idea, why not apply the flexbox layout algorithm to SVG? HTML and CSS have a number of different techniques for constructing layouts - if one of these could be applied to SVG it would make it possible to construct a chart without all of the manual computation seen above.
Just a couple of days after Chris suggested this approach I was watching the Facebook ReactJS Keynote, where they unveiled React Native. This framework allows you to write iOS and Android apps using ReactJS. During the development of this framework they had found the iOS constraints-based layout system to be quite cumbersome so replaced it with a flexbox implementation written in JavaScript. Their re-implementation of flexbox is open sourced as a separate project.
Perfect!
Applying Flexbox to SVG
The interface for Facebook’s css-layout
couldn’t be simpler, given a tree of nodes with associated CSS attributes, you invoke computeLayout
:
The resultant layout is computed after which each node is given a layout
property that describes its width, height and location with respect to the parent node:
Applying this technique to SVG is as simple as associating a style with each SVG element. This can be done by adding a custom attribute, layout-css
:
The following code constructs a suitable node-tree from the above SVG:
I have omitted the parseStyle
function which parses the layout-css
property constructing a JavaScript object.
Once the node tree has been constructed and the layout computed, all that remains is to apply this layout to the SVG. The top
and left
layout properties are applied as a transform, whereas the height
and width
are written to layout-height
and layout-width
attributes respectively. The reason for this is that SVG group elements (g
) have an origin but do not have a width or height. In order for a child element to occupy the rectangle defined by the layout mechanism, they need some way to obtain the computed bounds.
The following puts it all together as a function that can be called on a D3 selection
Applying flexbox layout to a selection is now as simple as the following:
With the simple SVG example above, the layout mechanism writes the required transforms, widths and heights as follows:
Creating a chart layout
Here’s a more complex example that shows how this technique can be applied to construct a suitable layout for a chart with a title, axes and legend:
The following code applies the flexbox layout algorithm, then adds a rectangle to each of the containers that have been constructed in order to help visualise the results:
This results in the following layout:
Computing the above armed with nothing more than the ‘margin convention’ would be quite a painful process!
Summary
It was a great coincidence that the ReactJS Native development resulted in the open-sourcing of exactly the component I needed for applying flexbox to SVG. If you are interested in using this code, you can find it within the D3FC repository. This is a project with a wider goal of making it easier to construct complex financial charts using D3. Our aim is to construct components that enhance D3 rather than wrap it (which would take most of its power away).
Anyhow, more on D3FC later …
Regards, Colin E.