Launched helga: skincare made happy! See early progress.

Home

a (pragmatic) accessibility checklist

An accessibility checklist you might actually use, or not.

01 June 2023

As 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.

Example of an inaccessible carousel

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).