NYC Film Permits: A Glitch challenge

See finished project

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.

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.

Preview of the finished data visualization project
View the finished project

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.permits} permits</li>\n`;

html += '</ul>\n';

console.log( html );
A preview of the text-only version of the data visualization project

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.

More from the blog

A tinted screenshot showing domains where I have followers and follow accounts blocked by one of Mastodon's instances.
A tinted, zoomed in screenshot of a JSON object showing server information about a Mastodon instance.
A tinted screenshot of an unlabeled scatter chart, with most data points grouped on the right.

📖 Visit blog