The problem
Our client, a large energy company, needed to present historical energy usage data to their customers.
They had many graphs and wanted a way to make them all accessible, so as not to fall foul of the American Disabilities Act (ADA).
Our solution
We came up with a new pattern that enabled our client to make all of their existing graphs accessible β since it works for any chart with an x-axis.
Here's the finished version:
This chart:
- Works for mouse users β the data appears in a tooltip when you hover over the bars
- Works for keyboard users β the data appears in a tooltip when you interact with the x-axis
- Works for touch users β the tooltip appears when you tap the x-axis
- Works for screen reader users βΒ the tooltip is read out and there is a short and long description of the chart
- For a bonus point, it takes an inaccessible charting library and makes it accessible.
Let's explore how we made this using the ustwo Inclusivity Principles:
ustwo's Inclusivity Principles
1. Level-up your gear
What existing tools could we use?
Ideally we'd find an accessible charting library that has accessibility baked in. We recommend Highcharts since it has a nice accessibility module you can enable (it's a shame it's not enabled by default).
Client requirements on this project meant we had to use Recharts. In 2023 (the year we completed this work) Recharts lacked accessibility support β only adding it in 2024.
Each chart was an SVG and exploring it with a screenreader produced β for want of a better expression β a jumble of gibberish.
So we had to come up with a bespoke solution.
2. Enjoy the patterns
What patterns exist for accessible bar and line charts?
The W3C has some general guidelines for accessible charts: "Complex images contain substantial information....a two-part text alternative is required.... the short description to identify the image and... the long description β a textual representation of the essential information conveyed by the image."
Pattern 1: provide a short description and a long description
We like the look of the HTML figure
element β after all, the first rule of ARIA is "don't use ARIA".
<figure>
<img alt="The short description of the chart" />
<figcaption>
The long description of the chart, describing any meaning conveyed
by the graph as a whole
</figcaption>
</figure>
Pattern 2: make the underlying data available to everyone
Recharts has a tooltip that shows the data when you hover over a bar. This is great for mouse users, but doesn't work for keyboard users or screen reader users.
This leads us nicely to our third principle:
3. Think beyond touch
How are we going to get the tooltip working for keyboard users?
If only we had an accessible tooltip component that supported keyboard users and screen readers... Good news: we do! (Please read Part 2 of our series Designing accessible components: Creating an accessible tooltip for a deep dive into how we built our accessible tooltip component.)
Our solution is to overlay the chart with our own x-axis comprised of Month labels with tooltips.
import React from "react";
import { BarChart, Etc } from "recharts";
return (
<figure>
<BarChart data={data} />
<ul className="accessible-x-axis">
{data.map((entry, index) => (
<li>
<Tooltip
message={`Energy usage for ${entry.month}: ${entry.usage} kWh`}
>
{entry.month}
</Tooltip>
</li>
))}
</ul>
...
</figure>
);
This brings all the benefits of our accessible tooltip to our bar chart.
4. Close your eyes
We are getting there! Let's use our fourth principle and use the chart with a screen reader.
It's time to apply our first pattern: a short and long description.
HTML figure
elements generally contain an image. We're going to employ a little trick here: wrapping the entire chart in a div
with role="img"
and an aria-label
of the short description.
Everything inside a containing element with role="img"
is ignored by screen readers (via an inherited role="presentation"
). So all the SVG elements that were previously bamboozling our screenreader and now safely ignored. NICE.
Let's just add the figcaption with a description of what the chart is communicating, in our case a downward trend in energy usage:
<figure>
<div
role="img"
aria-label="Bar chart showing monthly energy usage for January to May"
>
<BarChart data={data} />
</div>
<figcaption className="sr-only">
The chart communicates a clear downward trend in energy usage over
the five-month period.
</figcaption>
<div className="accessible-x-axis">
{data.map((entry, index) => (
<Tooltip
message={`Energy usage for ${entry.month}: ${entry.usage} kWh`}
>
{entry.month}
</Tooltip>
))}
</div>
</figure>
This does beg the question: how do we generate long descriptions for dynamic charts? Our options are:
- write a naive algorithm that e.g. looks for the highest and lowest values and generates a description based on that
- use a machine learning model to generate a description β which would likely be expensive
- use a generic description that β combined with the available label data β communicates the full meaning of the chart
In production we opted for the first option, a naive algorithm that looks for the highest and lowest values and generates a description based on that.
We hide this description from everyone other than screen reader users using a utility class sr-only
; this is a common pattern for hiding content visually while keeping it available to screen readers.
Finally, it's time to turn to our final principle:
5. Party party (test) party
Go ahead, try it out! You can:
- Hover over the bars to see the values (on desktop)
- Tab through the months to focus individual bars
- Click the buttons below to get the values read out
- Use a screen reader to explore the data
Please do send us any feedback or issues you encounter with this pattern, and we will happily update this page.
Conclusion
Inclusive design is about creating an experience that works for everyone, regardless of how they interact with your content.
By considering all users, we've created a bar and line chart that's truly accessible. And the best part? This pattern can be applied to any data visualisation with an x-axis, and can be used to augment any inaccessible charting library.
Zing!