How to Make Charts with SVG

In my first post about making charts, I looked at methods that solely relied on CSS. I argued that this wasn’t the best option in most cases; there are just too many tricky design and development hurdles to overcome. Generally speaking, it’s best to make charts with a combination of SVG, JavaScript, and CSS.

Why not canvas?

There are plenty of other ways in which you could make a chart for the web, most notably by using the canvas element. However, Sara Soueidan suggests avoiding this method, too:

HTML5 Canvas can also be used to create such visualisations, but the content of the canvas are not part of the DOM and are thus not accessible by screen readers. You would need to create a secondary content between the opening and closing <canvas> tags to serve as fallback and as accessible content. You need to also take extra measures to map the content and interactivity between the contents of the canvas and the fallback, so that screen readers know which element is being interacted with. So an HTML5 Canvas would require double the amount of maintenance. […] With SVG, you get semantics and accessibility as well as interactivity with JavaScript out of the box.

Yet there are alternatives to this standalone canvas approach. For instance, Filament Group made a jQuery plugin called Visualize, which grabs the data from a table element and then creates a canvas chart. This practice makes a lot of sense, even if the element alone is not best suited to the task of graph-making.

Why SVG?

The SVG image format isn't just for icons or simple images. It has advantages that apply to making charts, too. In our compendium of SVG we described the format's general advantages like this:

  • Small file sizes that compress well
  • Scales to any size without losing clarity (except very tiny sizes)
  • Looks great on retina displays
  • Design control like interactivity and filters

We can update this with two more key points that are useful for charts:

  • SVGs are accessible to screen readers (with a little bit of work)
  • There are plenty of SVG-based chart frameworks out there to help

Let’s get started. What’s the simplest approach to making a chart with SVG?

Charts with <img>

Making a chart with SVG can be as easy as designing one in Illustrator, or your vector-based design app of choice, export it as SVG, and popping it straight into the markup using an <img> tag:

<img src="chart.svg" alt="Hopefully you can impart equally useful alternate content here.">

This is great because it will look good and scale well. However, we’ll lose most of the benefits of inline SVG, such as accessibility and interactivity. Apart from alt text, our data won’t be read aloud by screen readers and the data points in the charts themselves won’t be able to be interacted with by mouse, touch, or keyboard input.

These problems suggest that we should use another SVG embedding technique if we want to gain as much control as possible over them. For example, what happens if we were working on a project like Death from Above, where the interactive nature of the graphs greatly helps us understand the data?

A gif showing the interface of the Death from Above website

To get the most out of SVG we need to take all of that code and place it directly into our markup. That way we can style the graph with CSS, control interactivity with JavaScript, and gain all the accessibility benefits of inline SVG.

(We could get similar benefits with <object> or <iframe> SVG embeds, but the concept is so similar let's move ahead with inline SVG.)

Bar charts

Each column of our graph will be contained within a <g> element (in SVG-speak this is just a group of related elements), inside each of these we’ll place a rect element that defines the shape of the column, and a text element which allows us to print the number to the screen. Here’s a finished example:

See the Pen Simple bar chart in SVG by CSS-Tricks (@css-tricks) on CodePen.

We can position these <rect> and <text> elements with the standard x/y coordinates, like so:

<svg class="chart" width="420" height="150" aria-labelledby="title desc" role="img">
  <title id="title">A bar chart showing information</title>
  <desc id="desc">4 apples; 8 bananas; 15 kiwis; 16 oranges; 23 lemons</desc>
  <g class="bar">
    <rect width="40" height="19"></rect>
    <text x="45" y="9.5" dy=".35em">4 apples</text>
  </g>
  <g class="bar">
    <rect width="80" height="19" y="20"></rect>
    <text x="85" y="28" dy=".35em">8 bananas</text>
  </g>
  <g class="bar">
    <rect width="150" height="19" y="40"></rect>
    <text x="150" y="48" dy=".35em">15 kiwis</text>
  </g>
  <g class="bar">
    <rect width="160" height="19" y="60"></rect>
    <text x="161" y="68" dy=".35em">16 oranges</text>
  </g>
  <g class="bar">
    <rect width="230" height="19" y="80"></rect>
    <text x="235" y="88" dy=".35em">23 lemons</text>
  </g>
</svg>

Notice that you can hover over the elements to change the color of the bar and the text color? This is possible with the fill CSS property:

.bar {
  fill: red; /* changes the background */
  height: 21px;
  transition: fill .3s ease;
  cursor: pointer;
  font-family: Helvetica, sans-serif;
}
.bar text {
  color: black;
}
.bar:hover,
.bar:focus {
  fill: black;
}
.bar:hover text,
.bar:focus text {
  fill: red;
}

Yay for simple, interactive SVGs!

There’s a problem here though: tabbing through the graph won’t work (because browsers doesn’t support the focusable attribute yet). Léonie Watson has accessibility tips for SVG that suggest you can use <a xlink:href="#"> links for focusability - but what if the focusable area isn't really a link?

We're digging into this more soon with upcoming articles.

Spark lines

Since spark lines are effectively tiny bar charts, we can use much of the same code as before to make this example:

See the Pen Simple bar chart in SVG by CSS-Tricks (@css-tricks) on CodePen.

This time to align each g element side-by-side, we can use an inline CSS transform instead (it’s probably best to stick to the standard x and y coordinates but this just proves it’s possible). Then we’ll change the height attribute on the rect element to display the data correctly, and push each element down from the top with the y coordinate. This should align each bar to the bottom of the spark line. Here’s an abbreviated code example:

<g class="bar" transform="translate(0,0)">
  <rect height="10" y="10" width="3"></rect>
</g>
<g class="bar" transform="translate(3,0)">
  <rect height="6" y="14" width="3"></rect>
</g>

Line charts

To markup the data points of a line chart we can use the polyline element and the points attribute:

<svg viewBox="0 0 500 100" class="chart">
  <polyline
     fill="none"
     stroke="#0074d9"
     stroke-width="3"
     points="
       0,120
       20,60
       40,80
       60,20"/>
</svg>

0,120 in this example would mean 0 from the left and 120 from the top of the SVG canvas. And once we have our list of data points ready then we can define the width of the line with stroke-width and the color of that line with stroke:

See the Pen e742b827fb9d04df80c56f3fea55c031 by CSS-Tricks (@css-tricks) on CodePen.

But this only styles the data – what about those vital lines that helps everyone decipher the points themselves? What about the axes?

Making the axes and labels

Roemer Vlasveld made a great tutorial about developing graphics with SVG and he documents some interesting properties that will help us label and style each axis of a graph. We’ll take a simplified version of his work for the example below:

<svg class="graph">
  <g class="grid x-grid">
    <line x1="90" x2="90" y1="5" y2="371"></line>
  </g>
  <g class="grid y-grid">
    <line x1="90" x2="705" y1="370" y2="370"></line>
  </g>
</svg>

Each g will be used to create the horizontal and vertical lines and, when combined with the correctly positioned text labels, this results in our basic styles and markup for a scatter graph, just without the data:

See the Pen 0c0f74831368af3bc93a8d146d85744e by CSS-Tricks (@css-tricks) on CodePen.

After adding each point of our data as a series of circles inside another g element we can see how this might look as a scatter graph:

See the Pen cb00290fb3ee28378498eca296d7c395 by CSS-Tricks (@css-tricks) on CodePen.

Pie charts

We’ve mentioned Lea Verou’s article about pie charts a couple of times because it’s an excellent primer to get us started. I won’t repeat her technique in detail here, although I do think it would be interesting to see how we might go about making these SVG pie charts interactive. Here’s a finished example to give you an idea of where we’re heading:

See the Pen Interactive SVG chart by CSS-Tricks (@css-tricks) on CodePen.

When one of those buttons above the pie chart is clicked, the chart will update with the value stored in a JavaScript object. This method may have accessibility issues, but what I want to focus on is the interactive combination of JavaScript and SVG.

First the markup:

<figure>
  <figcaption>
    Percentage of world population by continent
  </figcaption>
  
  <div class="buttons"></div>

  <svg width="100" height="100" class="chart">
    <circle r="25" cx="50" cy="50" class="pie"/>
  </svg>
</figure>

Then we can set up our population data, like so:

var continents = {
  asia: 60,
  northAmerica : 5,
  southAmerica: 9,
  oceania: 1,
  africa: 15,
  europe: 12
};

What we want to do here is populate the empty .buttons div with a series of buttons that, when clicked, will change the stroke-dasharray property of the circle SVG element. We can create those buttons like this:

var buttons = document.querySelector('.buttons');

for(property in continents) {
  var newEl = document.createElement('button');
  newEl.innerText = property;
  newEl.setAttribute('data-name', property);
  buttons.appendChild(newEl);
}

Next, we’ll need to fix those percentages since asia: 60 in our continents object means 60 out of 100 and not 60 out of the circumference of our circle. We can make a function to fix this for us:

var total = 158; 

var numberFixer = function(num){
  var result = ((num * total) / 100);
  return result;
}

From there we can add an event listener to each of those buttons and make a new function called setPieChart() that changes the value of stroke-dasharray by double checking the data-name attribute of each button and finding the corresponding continent in our object:

buttons.addEventListener('click', function(e){
  if(e.target != e.currentTarget) {
    var el = e.target,
        name = el.getAttribute('data-name');
    setPieChart(name);
    setActiveClass(el);
  }
  e.stopPropagation();
});

var setPieChart = function(name) {
  var number = continents[name],
      fixedNumber = numberFixer(number),
      result = fixedNumber + ' ' + total;
  pie.style.strokeDasharray = result;
}

And once we’ve added a few helper functions to add an active style to the buttons, we’ve got ourselves a fully functioning interactive pie chart:

See the Pen Interactive SVG chart by CSS-Tricks (@css-tricks) on CodePen.

Manipulating SVG with CSS and JavaScript

Animating the changes in the previous example was relatively straightforward, all we had to do was use the transition property in CSS, like this:

circle {
  transition: stroke-dasharray .3s ease;
}

Then, once we changed the property with our script, CSS would do all the animating for us. But what other SVG properties and attributes can be manipulated with CSS?

Well, one of the peculiarities of styling SVG with CSS is that there are only certain properties we can control. If we want to change the x or y coordinates of a g for instance (without using the CSS transform property) then we'll need to use JavaScript. What makes this even stranger if you’re unfamiliar with the SVG syntax is that CSS properties will have an affect on certain elements, but not others.

There’s a handy list of properties by the W3C that shows which property effects which SVG element, so make sure to double check that list if you’re not seeing what you expect when styling them.

Editing SVG by hand is not a perfect solution

With the other basic shapes, including rect, line and polygon we can make any type of SVG chart that our heart desires. The real question is this: do we really want to? For example, line charts are certainly possible to make when you’re editing an SVG by hand, but I wouldn't necessarily recommend it since the syntax is a bit complex, especially if you wanted to do something like curve the line.

Writing SVG by hand can be slow and frustrating. Even for simple charts, it takes forever to write the code and to visually position each section. Much like the CSS-only solution to making graphs, your experience of making a chart with SVG by hand is likely to be a painful experience unless you’re making something very small.

There must be a better way, right?

Frameworks to the rescue!

In an upcoming article we’ll be discussing all of the benefits (and problems) of using a charting framework to help us make them a little easier to produce. We’ll compare all the popular charting frameworks and see what it’s like to visualise data in a more productive and emotionally healthy manner.

More information