A quick look at how screen-readers read basic HTML links and some of hurdles you might encounter with these simple controls
For years I've been writing semantic HTML ensuring that the HTML output makes sense in a text-only browser and then trusting that screen-reads would be able to understand the content if it's semantically correct, was this assumption still correct? Historically JAWS (Job Access with Speech) was the primary screen-reader by market share and it was the go to screen-reader for testing. Now with the rise of NVDA (Non-Visual Desktop Access) on Windows (free to use) and VoiceOver on Mac screen-reader the market is more fragmented, with these three making up roughly 90% of the market. On one hand, this is great news for web-users as it means they have more choice, on the other it also means headaches for developers as screen-readers don't always read HTML consistently.
Screen-reader software
As a Mac user I have access to VoiceOver which is the de facto Mac screen-reader, for Windows I use a VM with JAWS, NVDA and every now and again Narrator (which is Windows built in screen-reader)
Note: screen-reader versions; JAWS 2020, VoiceOver Catalina, NVDA 2019.2.1, Narrator Win10
Screen-reader reading modes
Screen-readers are very complex beasts so without going into too much detail I'm just looking at 'Browse' and 'Focus' modes. 'Browse' (aka Virtual) mode is when the screen-reader reads out paragraph text e.g. web-page content and 'Focus' mode is when the user tabs through focusable elements e.g. a navigation or input form.
Firstly, a couple of developer/tester tips
Just as a side note it's very handy when testing a web-page to have a visual log of what the screen-reader has just readout. With NVDA you can view this by enabling the 'NVDA Speech Viewer' from the tools menu, in JAWS enable 'Speech history' then you can press INSERT+SPACE, followed by H key to view history and with VoiceOver the transcript is shown in the VoiceOver panel by default. If you are a Mac user testing on a VM, I recommend plugging in a standard Windows keyboard. True it's possible to configure screen-readers to use laptop keyboard and you can map keys using something like SharpKeys but personally I find having a real Windows keyboard way, way easier!
Links, lets look at what is readout
Here is a download document link example comprising of a document title ("My Document") with additional "download PDF" text we want screen-readers to read out, but visually we just want to show a download icon. I created a few HTML options to see how different screen-readers read them out. All the following links (apart from the control) I styled to display basically the same, either by using an inline graphic, fontawesome icons or background image on the link element and visually hiding the "download PDF" text where applicable. So all the links tested below look like this...
Focus mode | Browse mode | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
HTML example | JAWS, Chrome |
JAWS, Edge 18 |
JAWS, Edge 79 |
NVDA, Firefox |
NVDA, Edge 18 |
NVDA, Edge 79 |
Narrator, Firefox |
Narrator, Edge 18 |
Narrator, Edge 79 |
VoiceOver |
JAWS, Chrome |
JAWS, Edge 18 |
JAWS, Edge 79 |
NVDA, Firefox |
NVDA, Edge 18 |
NVDA, Edge 79 |
Narrator, Firefox |
Narrator, Edge 18 |
Narrator, Edge 79 |
VoiceOver |
|
||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
|
One other thing while we're at it... screen-readers create lists of links which is a very handy way for users to find content. So lets have a quick look at how our links behave when they are listed...
HTML example | JAWS, Chrome |
JAWS, Edge 18 |
JAWS, Edge 79 |
JAWS, Firefox |
NVDA, Chrome |
NVDA, Edge 18 |
NVDA, Edge 79 |
NVDA, Firefox |
Narrator, Chrome |
Narrator, Edge 18 |
Narrator, Edge 79 |
Narrator, Firefox |
VoiceOver |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|||||||||||||
|
|||||||||||||
|
|||||||||||||
|
|||||||||||||
|
|||||||||||||
|
|||||||||||||
|
|||||||||||||
|
|||||||||||||
|
What does the above mean? Basically you can expect your HTML to be readout differently depending on the reading mode and screen-reader + browser combination. Skip to conclusion
Note on semantic text
For screen-readers you can add a comma space to hidden text, screen-readers will then add a very small pause between the words. This will also prevent the start of the hidden text from merging with the end of the previous text, which can sometimes happen!
(I use Bootstrap screen-reader utility classes)
<a href="./document.pdf">
My Document
<span class="sr-only">, download PDF</span>
</a>
SVG support
The SVGs themselves are widely supported and vector graphics should be preferred over images, but lets just have a quick look at the following three inline SVGs which use the child Title, Description (desc) and Text elements to see which is readout by the screen-reader. The expected output is for the links is "link Document download" or "Document download link" and for the buttons "Close button" or "button Close" .
HTML example | JAWS, Chrome |
JAWS, Edge 18 |
NVDA, Firefox |
NVDA, Edge 18 |
Narrator, Firefox |
Narrator, Edge 18 |
VoiceOver |
|
---|---|---|---|---|---|---|---|---|
|
Title | |||||||
Description | ||||||||
Text | ||||||||
|
Title | |||||||
Description | ||||||||
Text |
Well, the interesting thing here is that the different elements where readout or ignored depending on the parent element, the conclusion of the above is that all three of these elements are best avoided! Instead I would include semantic text outside the SVG element and tell the screen-reader that the SVG is decorative by adding role="presentation" or aria-hidden="true" e.g.
...
<span class="sr-only">, download PDF</span>
<svg aria-hidden="true">
<!-- don't use title, desc or text here! -->
</svg>
...
Points to take-away...
- Screen-readers DO NOT readout content consistently
- I always assumed that a button/link containing an image with alt text would be a safe choice of markup, as it works nicely in a text only browser, but actually the alt text can be completely ignored in some screen-readers + browser + reading mode combinations
- Support for inline SVG child elements, title, desc and text elements is 'patchy' to say the least! Avoid
aria-label
isn't a silver bullet, it won't always be read depending on the reading mode + browser combination and if the element is a role based element or not- CSS content won't always be read and is probably best avoided for anything important
- The Title attribute doesn't get readout in all scenarios, so again, probably best avoided for important information
- Add leading comma space ', ' to semantic text
- Sometimes screen-readers will duplicate content and read it or bits of it twice, whilst this is irritating, it's not a show stopper as it is still clear what the link/button does and what will happen if it's clicked
- When testing it's best to test on a high frequency screen-reader browser combination like JAWS with Chrome or NVDA with Firefox as if an unusual combination
- Semantic HTML is still a safe option. Although, some screen-readers will read the elements separately e.g. "link (link text) link (semantic text)"
In conclusion
The almost 'bullet proof' link... whilst the semantic link is not 100% perfect as it causes some screen readers to
repeat the word 'link' it does at least have the 'download PDF' text added to all our test cases including the links
list. Whichever graphic format, background-image, icon, img or SVG you choose (for me, an inline SVG would be the best
choice, but that's another story!) hide it from the screen-reader by using the aria-hidden="true" or role="presentation"
attributes. The aria-label
attribute isn't necessary on semantically correct content.
<!-- semantic text with icon front -->
<a href="./document.pdf">
My Document
<span class="sr-only">, download PDF</span>
<i class="fa fa-download" aria-hidden="true"></i>
</a>
<!-- semantic text with inline image -->
<a href="./document.pdf">
My Document
<span class="sr-only">, download PDF</span>
<img aria-hidden="true" src="..." alt="" />
</a>
<!-- semantic text with inline SVG -->
<a href="./document.pdf">
My Document
<span class="sr-only">, download PDF</span>
<svg role="presentation">...</svg>
</a>
Further reading
Hero image by Cody Chan, opens in a new window