a (pragmatic) accessibility checklist
An accessibility checklist you might actually use, or not.
01 June 2023As of late, I've been learning more about accessibility. Like testing, it's often an after thought for us developers, but it shouldn't be. Why else call it user experience if the things we build don't account for all users?
But even if it's good for us, we won't do much of it if we think it's hard or difficult. This perspective is warranted because when learning web development there is often little or sadly no emphasis on accessibility aside from learning to use semantic HTML.
Improved accessibility means bots can more easily find information on our websites which might lead to better SEO rankings, and often times improvements in accessibility provide improvements for all users (think closed captions on your Netflix shows and YouTube videos - pretty handy when the people in the box start whispering).
So, in my own attempt to solve this problem I created this checklist. There are many out there, for example there’s one by WebIAM, The a11y Project, and even this interactive one by Julian Salas. But these checklists provide little information about the how part, and when you're starting out with accessibility it can be slightly overwhelming. This checklist tries to address that. It's undoubtedly non-exhaustive, and instead it's a list of all the checks I've started performing on my own projects. For each one I implement, I'll be sure to add to the list.
Will this or any other checklist make your websites and applications completely accessible? Well, not quite. There are a good start at making them more accessible, but a sure way of knowing if they truly are usable for people with disabilities is by asking them; going out into the world and seeing how they use your websites and applications. All else is just our best guess and sometimes our best guess might not be good enough.
And now, for the code.
Images should have alt
attributes, maybe
The Why
We probably all know it: add an alt
attribute to <img>
elements. But, have you ever wondered what would happen if you didn’t? Well, I recently learnt the answer to that. When <img>
elements don’t have an alt
attribute associated with them, screen readers describe the image using it’s file name. So now imagine your file names looked something like this: jibberishjibberishjibberish.png
. Screen readers would actually describe the image as “jibberish jibberish jibberish dot png”. That’s not helpful.
Of course we wouldn’t go around naming our files so unintelligibly, but even if we named them well, the names of our files are still no good substitute for properly labelling our images with appropraite alternative text.
The How
Linting can help with this. If you don’t already use a linter, I strongly recommend you look into one. There’s no greater joy than looking at a codebase that is consistent in styling, as though authored by a single person with a fine set of rules for how their code aught to look. No. Greater. Joy.
Anyways, linting. There is an ESLint plugin - eslint-plugin-jsx-a11y - which has a rule that enforces “all elements that require alternative text have meaningful information to relay back to the end user”. The Airbnb style guide (which seems to be a pretty popular option) includes this plugin in its setup, and so does the ESLint plugin by Next.js - eslint-config-next.
An Exception
Not all images require alternative text. This includes decorative images such as icons. But, this is only applicable if the icons have an accompanying label.
For example, a shopping cart button which has both a shopping cart icon and the label 'Cart' would not require the labelling of the shopping cart icon. The label 'Cart' would be sufficient so the icon can have an empty alt
attribute instead. Having both would be redundant and would be read as 'Shopping cart. Cart, button.'
However, the 'Cart' button might require a more descriptive labelling for screen readers, in particular using the
aria-label
attribute to set the button's label as 'View Cart' instead. Doing this mean screen readers would not narrate the 'Cart' label, and instead would narrate 'View Cart' to it's user.
If the button had no accompanying label, then the shopping cart icon would require an alternative text which can be set on the element's alt
attribute.
Label non-text content, maybe
The Why
Oftentimes we create custom widgets and these can have a number of accessibility issues if we are not mindful of accessibility when building them. One such widget which falls prey to such issues are those that have visual context and cues only, for example swipeable carousels, such as the one shown below.
In the implementation, we see something like this:
// Carousel pagination. <div className='flex flex-row justify-center space-x-1 lg:hidden'> {slides.map((slide, index) => { const isActive = currentSlide === index return ( <span key={index} className={`h-2 w-2 rounded-full border border-primary-orange ${ isActive ? 'bg-primary-orange' : '' }`} /> ) })} </div>
Firstly, a screen reader user wouldn’t be able to interact with this carousel because there is nothing to interact with. Replacing the <span>
element with a <button>
element might help, but now when interacting with the carousel each button is narrated as “button”. There’s no context concerning what pressing the button would do, and that’s a bit unhelpful.
Some of the widgets we create don’t have this context, or worse it’s not possible to interact with them at all.
The How
Luckily the eslint-plugin-jsx-a11y plugin also includes a rule that can help with this sort of issue and it dictates that “a control must be associated with a text label”. This can be achieved in a number of ways, such as adding an aria-label
attribute to the button as shown below.
<div className='flex flex-row justify-center space-x-1 lg:hidden'> {slides.map((slide, index) => { const isActive = currentSlide === index return ( <button key={index} type='button' aria-label={`View slide ${index + 1}`} onClick={() => {// Handle click.}} className={`h-2 w-2 rounded-full border border-primary-orange ${ isActive ? 'bg-primary-orange' : '' }`} /> ) })} </div>
There are also other ways of labelling content such as using the aria-labelledby
attribute, or adding content the the markup which is only visible to screen readers and is not visually shown.
Check the tab order
The Why
Ever tried filling out a form and while tabbing through it the form inputs seems to jump around? Instead of flowing in a chronological order as you’d expect, focus moves from input to input in a haphazard fashion. This issue isn’t (hopefully) all too common, but it does happen.
For people who primarily use the keyboard to navigate the Web, this can be increasingly frustrating. And when people get frustrated, they likely leave and find a better solution.
The How
When testing our applications, we should also tab through them and check if the order in which a keyboard user would navigate through the application is as expected. The elements may be visually in the correct position, but in the DOM tree they aren't (CSS or JS being a likely offender, or some misplaced tabIndex
attribute which is making elements jump in and out of the normal tab order).