Originally published on the Tenon.io blog, on November 25, 2019, in modified form.
Charts, graphs, and other data visualizations are an increasingly common way to convey complex information quickly to your audience. They’re eye-catching, helpful to your users, and they generate lots of traffic to your site.
And they’re inaccessible.
They don’t have to be, but almost all data visualizations are hiding crucial information away from your users with visual impairments. You can fix that, and I’m writing this post to help you along.
My name is Doug Schepers. I worked for the World Wide Web Consortium (W3C), the web standards organization, for a decade writing technical specifications and running working groups. I saw first-hand the disconnect between standards and real-world implementations, and the pain that caused developers and their users. I started W3C’s developer relations activity, before leaving W3C to form a startup doing accessible data visualizations.
One of the projects I worked on at W3C was graphics accessibility, specifically SVG accessibility. I first realized how you could make accessible charts in 2009, and after trying to get something standardized for years, I spoke about it at OpenVis Conf and CSUN in 2013. That got a lot of people excited, and we formed a task force to lay the groundwork, building on ARIA. Standards are slow, but in 2018, W3C published the WAI-ARIA Graphics Module, mostly due to the efforts of Amelia Bellamy-Royds, a dedicated and brilliant SVG expert. Even if you know about ARIA, you probably haven’t heard of the WAI-ARIA Graphics Module, unless you’re especially geeky.
In fact, you might have heard of ARIA, but might not know much about it. Welcome to the club. ARIA is very useful, but widely misunderstood and misused, and it lacks great documentation. In future blog posts, we hope to help clear up ARIA for you, and make using it painless (well, mostly painless, once the numbness sets in).
ARIA basics
At this point, you might be wondering why I’m talking about opera singing. That’s not the kind of aria we usually mean in accessibility circles. ARIA stands for Accessible Rich Internet Applications. It’s a web standard from W3C’s Web Accessibility Initiative (WAI). You’ll sometimes see it called WAI-ARIA.
ARIA is essentially a set of extensions to HTML that help make your web content and controls more usable to people with disabilities. ARIA defines attributes that apply additional semantics and behavior to elements.
- The
'role'
attribute defines the element’s assigned type of user interface component (e.g. a checkbox) - The various property attributes describe that component’s individual characteristics of (e.g. whether it’s required)
- The different state attributes define that component’s current mode (e.g. if it’s checked or unchecked)
ARIA for graphics
So, what does this have to do with charts? Especially a static, non-interactive chart?
Some of those ARIA roles defines significant parts of a document, called landmarks. Landmarks are things like headers, footers, sidebars, navigation, and so on. People using a screen reader often move around these landmarks to build up a complete picture of a document or application, one component at a time.
A chart is just a special kind of document, and it has its own structure and landmarks. For a bar chart, that’s the title, the x-axis and y-axis, and each bar.
The WAI-ARIA Graphics Module defines 3 new roles:
'graphics-document'
: a document that conveys its meaning through its visual appearance'graphics-object'
: a section of a graphics-document that represents a distinct object or sub-component with semantic meaning. A graphical object may itself have nested sub-components'graphics-symbol'
: a graphical object treated as a single image component, used to convey a simple meaning or category
Let’s drill into each of these a bit more.
'graphics-document'
This is the parent node of all other ARIA graphics elements. In the case of a bar chart, this is the bar chart itself.
Don’t confuse this with the root node of the document, like the <svg> element (though it might also be that). Each chart should have a single ‘graphics-document’ root, though one document might contain multiple 'graphics-document'
elements, such as a dashboard file with several different charts in it.
'graphics-object'
This is an element that contains other parts. It’s a generic structural container element, like an <article> or <section> element in HTML.
This can be used for the x and y axis, for example. They are well-defined areas of the document, which contain other sub-components, like tick marks and scale labels. For this case, the best analogy might be the <thead> element in HTML: a table header, which contains one or more rows each with more or <th> (table header) elements.
'graphics-symbol'
This is an atomic bit of code, usually some mark, icon, shape, or line in a chart, which represents the data point value. In a bar chart, each bar would have a role=’graphics-symbol’ attribute, as would each slice in a pie or donut chart, each line in a line chart, each dot or symbol in a scatterplot.
This doesn’t mean that the data point representation has to be simple, or a single element. If you have a pseudo-3D bar chart, each bar might have elements for the front, top, sides, and shadow, but it should all be encapsulated in a single group (in SVG, a <g>
element) with the 'graphics-symbol'
role.
And that’s it! If you mark up an SVG chart with these three ARIA roles, it suddenly becomes accessible!
Just kidding, that’s just the beginning. By themselves, these roles don’t have any properties or states; in other words, they don’t have any inherent behavior. But what they do is to identify each part to the screen reader, so it knows how to categorize and characterize them to the end user.
Exposing elements to the user
Once you have the parts of the chart document marked up, it’s getting more accessible. I say “more accessible”, but that’s not true yet, because they are still invisible to screen readers. By default, most SVG elements are hidden from screen readers, the exceptions being links and textual elements. You can make them keyboard-navigable by adding tabindex='0'
to each component.
Adding value
What’s the best way to add value for a screen reader user? By literally adding the value of the data point to its representing element. If you have an element that draws a bar, with the height showing the value, then you simply include that value as text as well; think of it as really tiny alt-text. In SVG, there are four ways to do that: with a <text>
element; with a <title>
element; with an 'aria-label'
attribute; or with an 'aria-labelledby'
attribute pointing to an element that holds the value (such as a <text>
or <title>
element).
<text>
element
The <text>
element (and its optional child element, the <tspan>
) is how you render text to the screen in SVG. Like in HTML, this text can be selected, copied, and searched for. This is ideal if you want the value to always be shown, as a label on a bar or pie slice. To associate it with the data point element, group it in the same <g>
element, or give it an 'id'
attribute and point to it with the 'aria-labelledby'
attribute.
<title>
element
The <title>
element in SVG is like the ‘title’ attribute in HTML. It’s a hidden metadata value that only appears on hover (note that it doesn’t appear on focus, so it’s not as accessible as it should be). You can use this if you only want the value to be shown on hover. To associate it with the data point element, put the <title>
as a child of the data point element or its parent <g>
element, or give it an 'id’
attribute and point to it with the 'aria-labelledby'
attribute. (Note that for legacy browser support, it might be best to use both of those techniques.)
'aria-label'
attribute
This is pretty straightforward. Just put the value in the 'aria-label'
attribute, like this:
<rect aria-label="4" … />
'aria-labelledby'
attribute
This is a tad more complicated, but there are reasons you might want to have this level of indirection, such as when you’re pointing to a value that appears elsewhere and don’t want to duplicate it. You give the referred element an ‘id’ attribute value, and use that as the value for the ‘aria-labelledby’ attribute, like so:
<text id="bar-1-value"></text>
<rect aria-labelledby="bar-1-value" … />
Any one of these techniques will cause the screen reader to read out the value. Voila! Now, the chart is well on its way to being about as accessible as a table. The user can use a keyboard to get to each component, and step through each bar in turn to hear the values.
These are the bare essentials for making an accessible SVG chart. It’s worth noting that that the WAI-ARIA Graphics Module isn’t SVG-specific; you could do the same with HTML+CSS charts. But SVG is the markup of choice for charts these days.
Once more, with meaning
Okay, so now the user can get at each data point, and get its value. Just like in a data table. But a chart isn’t a data table, and not each data point is the same. When a blind or low-vision person is talking to a fully-sighted person about a chart, they should be able to use the same terms to avoid confusion. They should know it’s a bar in a bar chart, a slice in a pie chart, a line in a line chart. They should know it’s the y-axis or the x-axis or the legend. They should know what kind of chart it is.
ARIA doesn’t have specific roles for different kinds of data points, or parts of a chart, or chart types. But what it does have, starting in ARIA 1.1 (finalized in December 2017), is the 'aria-roledescription'
attribute.This attribute lets you provide a natural-language equivalent for an ARIA role.
As an example, the following element would be read by a screen reader as something like, “bar element”, instead of “graphics-symbol element”:
<rect role="graphics-symbol" aria-roledescription="bar"
tabindex="0"
x="70" y="20" width="25" height="40"></rect>
In SVG, this is a <rect>
element, which draws a rectangle on the screen. But to a screen reader, it’s now a bar element, and that’s how it’s communicated to the user. With 'aria-roledescription'
, you can call a shovel a shovel.
Does this work in browsers and screen readers?
Standards are one thing, and implementations in “user agents” (browsers and screen readers) is another story. Fortunately, these features have pretty good support. That raises the question, what does “support” look like in the case of ARIA, and for these features specifically? Especially when they don’t have any defined behavior?
To answer that, we need to step back and look at how Assistive Technology (AT) like a screen reader works. In short, the browser downloads the document file, then creates a DOM (Document Object Model) tree, and visually renders the result of that DOM tree; that’s all commonly understood by web developers. But less commonly known is that from the DOM tree, the browser also creates an accessibility tree (also called the accessibility object model, or AOM), which is a subset of the DOM tree that is specifically relevant to AT. The browser then exposes that accessibility tree to its analog in the operating system, the platform-specific Accessibility API (each unique to that OS). The OS Accessibility API in turn shares that information with the AT, such as a screen reader, which is finally rendered (as speech, Braille, or some other form) to the end user.
So, this means that the question of user agent support becomes, “Does the browser translate this element correctly to its analog in the platform Accessibility API? And does the screen reader recognize and announce that role?” That’s something testable.
This translation is detailed in the Graphics Accessibility API Mappings, a companion spec to the WAI-ARIA Graphics Module. And that specification has a test suite, which tells us how well it’s supported. At the time of publication in October 2018, all browsers on all OSes supported these features, with the exception of the 'aria-roledescription'
attribute in Chrome on MacOS. Since then, my testing shows that that also seems to have been implemented.
Full disclosure
I have noticed glitches in browser and screen reader support. This is true of ARIA support in general. There is a legitimate question on how we best serve our readers: Do we take a conservative approach, and only publish content that is rock-solid? Or do we recognize that if we don’t create valid content that pushes those boundaries, browser and AT vendors don’t have enough incentive to improve their support? That’s a decision that each publisher needs to make. For my part, I favor the latter, to counter the chicken-or-egg debate. No progress happens without active effort.