Accessibility testing as a screen reader user

When testing a website or mobile application’s accessibility, some might say that someone who routinely uses a screen reader would be best placed to eke out issues, right? While it’s true that if you use a screen reader for everything you do on your computer, all the time, you naturally become an expert user over time, and you can find issues that may otherwise fly under the radar. Also, you tend to do certain tasks faster than an ad-hoc screen-reader user or tester would. But depending on the site or app under audit, full-time screen-reader users might not automatically be in a better position to spot issues.

In the worst-case scenario, when content is completely inaccessible, a totally blind auditor may not be able to process enough of what’s on the page to carry out any meaningful testing. Even auditing content that’s reasonably accessible has its challenges when you can’t see the screen to compare your understanding of the content with what’s visibly presented to the user.

I’m a blind accessibility engineer at TPGi, and I’ve been carrying out accessibility audits and tests in one form or another for almost 25 years. The landscape has changed beyond recognition since I began. In the first formal accessibility test that I carried out, I used Internet Explorer 3.0 and the JAWS 3.0 screen reader. The most common accessibility issues were images with missing or inadequate alternative text, form fields without labels, and text that looked like headings but wasn’t marked up as such. Accessibility testing wasn’t a complicated affair (and to be fair, the same old issues still present themselves to this day).

Fast forward to the days of custom widgets and frameworks, shadow DOM components, multiple ways to render images, and exponentially more complex testing scenarios. So what are the most serious blockers for blind testers, and are there ways to work around them? Below are just some of the issues I encounter that uniquely affect me as a blind tester.

Controls exposed as plain text


During testing, I frequently come across controls that look like links, buttons, checkboxes, and the like that don’t expose themselves as such to JAWS. Their function is usually to request details or confirmation from the user or to reveal extra information. Since they aren’t native interactive elements, they usually aren’t part of the tab order, and they don’t show up in JAWS’ forms list or links list. So they’re easy for a screen reader user to miss. Here is a classic example:

<p>We are experiencing high volumes of orders at the current time so wait times may be a bit longer. <span class="button">Learn More</span></p>
A div element styled to look like a button with CSS that is not actually a button

This looks like a button, but it’s not keyboard focusable. But the surrounding content and the class name provide some hints as to what the element is supposed to be.


The text labels assigned to these controls often give the game away. “Learn More” and “Full description” are labels that I assume must be disclosure widgets that would reveal extra information if I could just activate them, while “I am over 18” and “I agree to the terms and conditions” are most likely checkboxes that need to be checked before I can submit a form. So I activate the JAWS cursor, move it to the control and use JAWS to fire a click event on it. If more information appears, or if I’m suddenly able to submit a form, then happy days!

If not, I can always inspect the markup using Chrome’s or Firefox’s developer-tools panel, where I can traverse the DOM and look out for visually hidden <input type="checkbox"> or <input type="radio"> controls, class="checkbox/radiobutton" attributes, or <div> elements close to the element at hand that contain hidden content that might be exposed if the control was clicked. Failing that, I’ll check whether the element or one of its close neighbors has an attached click event. If none of these exist, I would have to revert to the old tried and trusted method of asking a colleague to check whether the element was interactive.

How it should be done

All interactive elements should expose their roles to screen readers and other assistive technologies, ideally by using native HTML elements such as <a> elements for links, <button> and <input type="checkbox">. It’s always better to use native controls where possible because keyboard functionality comes baked in. If for some reason native elements can’t be used, a role attribute should be added to the control, for example <div role="button">Learn more about high volumes of orders</div>, but this does then require additional JavaScript development work to provide keyboard support for Space or Enter (depending on the type of control).

States defined visually but not structurally


When undertaking a multistep process—such as creating a user profile or checking out an online shopping basket—the steps in the process are usually listed above the content, with the active step indicated in some way (such as a color change or bold text). But there’s often no structural way provided for a screen-reader user to know which step is the current one. Here’s a typical example:

<ol class="steps">
 <li>Contact details</li>
 <li class="current">Payment details</li>
 <li>Authorize payment</li>

Visually it might look like this:

A set of three steps in a process with the current step indicated by bold font

While ‘Payment details’ is not conveyed to JAWS to me as the current step, I do have a trick up my sleeve.


JAWS has two great features that I couldn’t do without during testing:

  • Say Font (shortcut key JAWS key + F) describes the font: its name, font weight, text spacing and alignment.
  • Say Color (JAWS key + 5) tells me the foreground and background colors of the character that the cursor is on.

So I can use JAWS to check the colors and font weight of an unselected item, and compare it with the same attributes of the item relating to the step that I deduce is currently active, which more often than not gives the game away. If this doesn’t tell me anything useful, I inspect the selected element’s markup in Chrome’s or Firefox’s developer-tools panel. And if it has a CSS class that’s different from that of unselected items, I’ll find the class in the CSS and work out what visual changes it makes. This all sounds very long-winded, but it doesn’t take long at all, and it often gives me insider knowledge when recommending a solution.

How it should be done

Add an aria-current attribute to the element that represents the current step, page or location in a group of related items. For example:

<ol id="steps">
 <li>Contact details</li>
 <li aria-current="step">Payment details</li>
 <li>Authorize payment</li>

And here is a visual representation of the steps:

A set of three steps in a process but this time the current step is not indicated by bold text

Note that in this revised example, the font isn’t set to bold anymore, but I don’t need to rely on JAWS’ font tools now—the current step is conveyed to me using the aria-current attribute, and the step’s visual representation is no longer something that I need to rely on.

Taking screenshots


If I’m reporting on an accessibility issue I’ve discovered, it’s usually helpful for the developer if I can take a screenshot of the element and perhaps some surrounding content to provide some context. But how can a blind tester crop a screenshot to a specific area of the screen?


Bring on Firefox and its excellent Screenshot Node function. Developer tools panels are my friend, and Firefox often comes to the rescue here. If I inspect the element I’m interested in, Firefox has a function that can take a screenshot of only the selected node. If I need a bit more context, I can always take a screenshot of the parent node, which often provides the context I need.

The ‘Screenshot node’ menu item in Firefox Developer Tools and the node that will be captured is visually highlighted.
The node captured from the screenshot menu (saved to Downloads folder by default).

If I’m not sure I’ve captured the right content, I’ll find the screenshot on my hard drive, then press JAWS key + Space, O, F to run a JAWS OCR scan on the screenshot and make sure it contains the content I want. This isn’t a perfect solution by any means, and there are times when I have to ask a colleague to take a more precise screenshot, to crop the one I’ve taken, or possibly to annotate or highlight a specific part of the image. But it means I can take most of the screenshots I need without input from anyone else. It’s a good feeling!

Tests that are problematic using a screen reader

There are a few very visual tests that just aren’t feasible for me to tackle. It wouldn’t be easy for someone who can’t see the screen to, for example, test whether content is cropped or overlapped at different screen sizes, resolutions and orientations. Taking a screenshot and running it through the JAWS OCR function can provide some insight into whether content that should be on the screen is missing or incomplete, but I can’t be precise enough to write up a robust description and recommendation of the problem.

I also tend to avoid issues involving colors, fonts and text spacing, as well as images containing complex content such as graphs, charts and diagrams. Where possible, I don’t test for keyboard focus either, because JAWS is very good at plugging gaps by, for example, adding elements to the tab order when they may have inadvertently been excluded by the developer.


Working with a colleague at TPGi, we created a spreadsheet listing all the WCAG 2.1 levels A and AA success criteria, and we split them into issues that I can reliably test for and those where I can’t provide reliable results (Word docx file). Between us, we worked out that I can test for around two thirds of the 2.1 success criteria. Naturally, in my ideal world, I would be able to carry out a complete audit, and I’ll continue to push boundaries and come up with solutions. But even I have my limitations.

Categories: Development
Tags: , ,

About Isabel Holdsworth

Isabel has been a web developer and tester since 1997. A screen reader user herself, she is “selfishly passionate“ about helping other developers to understand what accessibility looks like and how to implement it.


Roberto says:

This is an awesome article. Thanks a lot for sharing some of your workarounds. Something I would add is that, as a screen reader tester, it is very useful to be well versed in the use of different screen readers and the way they handle web content. For example, when it goes to testing for SC 3.2.1 On Focus, I find the ability of NVDA to control whether the system focus follows NVDA’s browse cursor to be very handy.

Wow, I’ve been a firefox user forever, since I switched from Mosaic and Netscape Navigator in the previous millenium. Obviously, the screenshot node feature was not there 20 years ago. I consider myself to be a pretty advanced firefox user but I’ve never noticed screenshot node on the context menu before. Looks like a really cool feature. Thanks for sharing it.

Priti Rohra says:

Thanks Isabel for a brilliant article!
I often use few more so sharing – ttcheck keyboard focus order, press Insert + Z to turn virtual PC cursor Off and than try to navigate the page using Tab key. This helps to catch tabbing order/focus order issues as well as keyboard trap related issues! Additionally, I use Accessibility Tree of Chrome, FF and even handy features of Jaws such as Element information and Advance Element information Ins + Shift + F1 and Ctrl + Shift + Ins + F 1 respectively.

Thanks for the wonderful article! Being a screen reader user myself there are some learnings that I am taking away from here. I never knew about the screenshot feature & I am doing to give it a try. The other techniques that are laid out are also very good & will come in handy for my next assessment. I also sometimes rely on my sighted friends to get few things done during an assessment. I hope to go through your doc & will get more insights from it.