Glitch recently announced bi-weekly challenges, and as a huge fan of the site/platform and having recently started playing with data visualization, I had to, naturally, join in.
Our first challenge is for Data Visualization using @Chartjs. Learn how to make interactive charts and graphs with our how-to tutorial and remixable starter app, then try out your skills by tackling our challenges https://t.co/Bk7qFAOMDi 📈 https://t.co/EX1Thl0jkz
— 🎏Glitch (@glitch) February 19, 2019
I joined the @glitch data visualization @Chartjs challenge and learned about making accessible charts.
— Stefan Bohacek (@fourtonfish) February 26, 2019
Finished project: https://t.co/XHzC7Kav8B
Blog post: https://t.co/GFeGF0HcCk pic.twitter.com/13dxVm4yG6
For this project I wanted to use some local data. NYC Open Data has a big variety of interesting datasets about New York, including film permits issue by the city, which is what I chose to go with.

I made a few charts that let you examine the dataset from various angles: you can see the distribution of film permits across all five boroughs, the rise of issued permits over the years, and a breakdown by the type of film production and the country where the film projects originate.
All in all, this a pretty straightforward visualization, so, as an extra challenge, I decided to make the charts accessible.
The biggest limitation here is that chart.js, a JavaScript library chosen for this challenge, uses the canvas
element to render charts (see the Accessibility concerns section). You can add a fallback content to canvas
, which will be rendered when JavaScript is disabled, but from what I can tell, this text is not read by screen readers.
To work around this, I started by adding the fallback text. I wrote this little script to generate the HTML from the dataset JSON objects:
let html = '<ul>\n';
permitsByBorough.forEach( function( datapoint ){
html+= `<li>${datapoint.borough}: ${datapoint.permits} permits</li>\n`;
});
html += '</ul>\n';
html += '<ul>\n';
permitsByYear.forEach( function( datapoint ){
html+= `<li>${datapoint.year}: ${datapoint.permits} permits</li>\n`;
});
html += '</ul>\n';
html += '<ul>\n';
permitsByType.forEach( function( datapoint ){
html+= `<li>${datapoint.type}: ${datapoint.permits} permits</li>\n`;
});
html += '</ul>\n';
html += '<ul>\n';
permitsByCountry.forEach( function( datapoint ){
html+= `<li>${datapoint.country}: ${datapoint.permits} permits</li>\n`;
});
html += '</ul>\n';
console.log( html );
I added the HTML produced by the script inside the canvas
element, and then used it to update the aria-label
attribute of the canvas
with JavaScript. This way, if JavaScript is disabled in the browser, the text will be displayed and read out, and if JavaScript is enabled, the text will be used as a label for the canvas
element, and still picked up by screen readers.
let canvasCharts = document.querySelectorAll('.chart-container canvas');
for ( let i = 0; i < canvasCharts.length; i++ ){
let fallbackContent = canvasCharts[i].textContent;
let currentLabel = canvasCharts[i].getAttribute( 'aria-label' );
canvasCharts[i].setAttribute( 'aria-label', `${ currentLabel }: ${ fallbackContent }` );
}
And adding a tabindex
attribute to canvas
lets the user switch between charts with their keyboard.
As a final step towards accessibility, I went over to ColorBrewer and picked an accessible color scheme for my charts.
I tested my charts using the Color Oracle app for macOS and the results look pretty good.
I’m by no means an expert for accessibility, so if I missed anything, do let me know!
And here’s the finished project.