Contextually Marking up accessible images and SVGs
Before we begin, I want to level set that this is not an article about why you need to make images and SVG elements accessible. There are plenty of great resources already out there that provide excellent guidance, and I will include links to some of them in the further reading section at the end this article.
What this article is about is that there are various methods that can be used to provide an accessible name to an image or SVG element. Those being:
- An image’s
- A SVG’s child
aria-labelledbyon an image or SVG.
- Using a
titleattribute on an image or SVG.
However, depending on the markup pattern and the method used, the accessible name may not be exposed as expected, due to quirks or gaps in implementations. This means that without testing, graphics may not be exposed in the manner in which you might expect.
Additionally, this article will outline why different markup patterns are necessary when using images or SVG elements within interactive elements, such as links and buttons.
Examining different scenarios
In researching for the most robust patterns to markup graphics, I tested with different browsers and paired them with different screen readers on desktop and mobile devices:
Listing of tested browser & screen reader pairings
- macOS VoiceOver with Safari and Chrome
- Narrator with Edge
- NVDA with IE11, Firefox, Chrome and Edge
- JAWS with IE11, Firefox, Chrome and Edge
- TalkBack 7.2 on Android 8.1.0 with Chrome and Firefox
- iOS VoiceOver with Safari
Note: all Windows testing was performed on a Windows 10 machine.
Testing the image element
The image element has been around awhile now, so you may be wondering why I’d go to the trouble of testing the accessibility of such a fundamental part of the web. It turns out that depending on how you intend to use the element, and what file format you use as its source, you may create behaviors you weren’t anticipating.
Images that convey information
For raster graphics that are used to convey useful information, using the
alt attribute is the best way to convey the accessible name of the image element.
<img src="my-image.jpg" alt="Accessible Name">
alt attribute is recommended over other attributes that would provide an accessible name. While
aria-labelledby attributes could also provide an image element with an accessible name, only
alt will consistently render fall back text if an image were to break or be blocked.
Notes on image fall back text
As of publishing, Internet Explorer 11, pre-Chromium Edge and Firefox do not render
title attribute text for broken images.
Chrome and Safari will render fall back text for
title attributes. However, Safari will only show the fall back text if the image also has a defined fixed or minimum height and width which would provide enough visible space for the text to appear on screen. In other words, no set height and width on the image means no fall back text will be rendered.
alt attributes are used, the
alt will be the fall back text that is rendered.
Updated: Image elements with SVG source exception
For quite some time, there was a quirk with how image elements were exposed in Webkit if the source of the image was a .svg file.
<img src="my-file.svg" alt="hi">
Per the above markup, desktop Safari and VoiceOver used to announce “hi, group” with no announcement of the element being an “image”. However, what was more problematic was iOS Safari and VoiceOver used to skip over the element all together, as if it were decorative.
See the issue that was filed with WebKit in July 2018 due to this behavior.
In early 2021, an update was finally made to address this behavior, though this update was not publicly available at that time due to Safari’s release schedule. However, as of September 2021, the update has been confirmed with Safari 14.1.2 on macOS 11.5.2, and iOS 14.7.1. An
<img> with a .svg source now properly announces as an ‘image’ with the
alt providing its accessible name. If provided an empty
alt, the image is correctly ignored by VoiceOver as
alt="" would indicate it as decorative.
Image elements that are decorative
Graphics that serve as decoration to accompanying text should often be ignored by screen readers to help reduce unnecessary or redundant information. For image elements, the following markup can be used to make screen readers ignore decorative graphics:
<img src="img.jpg" alt=""> <!-- or --> <img src="img.jpg" alt>
Prior to macOS 10.14.4,
aria-hidden="true" would also be necessary to completely hide a decorative image to VoiceOver when paired with Chrome. Without the ARIA attribute, VoiceOver would still focus the image, but provide no announcement. Review your own analytics and perform your own testing to determine if this extra precaution is applicable to you.
Image elements within links
In a situation where both a graphic and visible label are children of a link, the image within the link can be considered decorative. This is because we don’t want to produce duplicate announcements for a link.
For example, the following would announce “Search” twice, and then likely announce the “graphic” or “image” role, accompanying the “link” role. The announcement would depend on the screen reader being used.
<!-- don't do this --> <a href="#urlHere"> <img src="fileName.fileType" alt="Search"> Search </a>
Giving the image element an empty
alt will treat it as decorative content. The visible text will provide the accessible name to the link:
<a href="#"> <img src="fileName.fileType" alt=""> Search </a>
In a situation where a graphic alone visually represents a link, there’s no need to add visually hidden text, or to use ARIA attributes to provide the link’s accessible name. The following markup pattern will provide the link with the accessible name, “search”, with each tested browser and screen reader pairing:
<a href="#urlHere"> <img src="fileName.fileType" alt="Search"> </a>
This pattern will allow screen readers to quickly access the element if navigating by links, or by graphic elements, with one exception. Chrome paired with JAWS 2019 (April) will not be able to navigate to this pattern when using the graphic hot key.
Image elements within buttons
One would be correct to assume that an image alone that provides the accessible name to a button should be no different than the markup used to provide an accessible name to a link. However, assumptions are exactly why I performed these tests…
<!-- should be able to use this... --> <button> <img src="fileName.fileType" alt="Accessible Name"> </button>
Presently, with pre-Chromium Microsoft Edge, a bug exists where the accessible name of the button is not exposed if provided by an image element’s
alt attribute. Until Edge is updated to be powered by Chromium, one of the following patterns can be used to mitigate this gap, while also maintaining a consistent accessible name announcement in other browser and screen reader pairings.
<!-- provide a title attribute as well --> <button> <img src="icon.fileType" alt="Accessible Name" title="Accessible Name"> </button>
In this pattern, the
title attribute will stand in for Edge not recognizing the image element’s
alt attribute. As long as the
title is exactly the same (content and capitalization) then all other screen reader and browser pairings will continue to announce the
button as expected. You can read more about the quirks (and UX gaps) of the
title attribute in my article The Trials and Tribulations of the Title Attribute.
<!-- or use ARIA --> <button aria-label="Accessible Name"> <img src="icon.fileType" alt=""> </button>
As children of
button elements are meant to have their semantics set to presentational, graphics within
buttons are not meant to be discoverable elements. That being the case, an
aria-label attribute could be set to the
button, and the graphic could be considered decorative. However,
aria-label may not be ideal, as in the event an image breaks, no fallback content would be displayed in the
Adding text along with a decorative graphic to the
<!-- display the accessible name with the icon --> <button> <img src="icon.fileType" alt> Accessible Name </button>
In this example, the accessible name for the
button is provided by the visible text. Because of this the graphic can be considered decorative, and no additional attributes would be necessary.
Additionally, the text could be visually hidden if the
button needed to be visually represented by the graphic alone.
<!-- use CSS to hide the text of the button --> <button> <img src="icon.fileType" alt> <span class="sr-only" role="presentation">Accessible Name</span> </button>
Note about using
.sr-only within interactive elements
display: block (or
position: absolute as the
sr-only class uses) can create multiple virtual cursor focus stops for buttons and links with some screen readers.
role="presentation" to the child element(s) with the
sr-only class will help make sure the virtual cursor does not stop multiple when using the Up arrow or Down arrow keys to navigate to or through an interactive element.
These tests treated SVG and image elements as similar types of content (graphics providing meaningful content or being used for decoration). However, SVGs fundamentally require different markup patterns to the image element, and thus necessitated different tests to be run.
In contrast to raster images, SVGs can have child elements like
<desc> which all may be used in exposing information to assistive technologies. Regarding the scope of these tests, only
<title> elements were used in the markup patterns they appear in.
For additional testing information on SVGs, which go beyond what I was looking to verify, please review Creating Accessible SVGs by Carie Fisher, SVG Icons and Screen Reader Accessibility (slideshare), by Dennis Lembree, and Accessible SVG flowcharts, by Léonie Watson, for starters.
With that said, let’s look at the patterns tested:
SVGs that convey information
The following pattern was one of the most robust in appropriately announcing an SVG an “image” or “graphic” with an accessible name. You might use this if marking up a logo or illustration that provides meaning to a document:
<svg role="img" focusable="false"> <title>Accessible Name</title> <use xlink:href="#svg-id-to-reference" aria-hidden="true" /> <!-- if not using <use> then the child elements of the inline SVG would go here --> </svg>
This pattern will properly announce the SVG as an “image” or “graphic” in all tested screen reader and browser pairings. Its accessible name will be properly announced, and it is even revealed as a native HTML tooltip on mouse hover.
If a native HTML tooltip is not desired, the following markup pattern could be used instead:
<svg role="img" aria-label="Accessible Name" focusable="false"> <use xlink:href="#..." aria-hidden="true"></use> </svg>
<use> stops some screen readers from making redundant announcements, or announcing a
<title> element that may be an unwanted child of the referenced SVG, or in some cases, causing a bug where the accessible name is ignored all together!
It’s worth noting that SVGs are often exported from graphics programs with unhelpful
<title>s (“Created by Sketch” is not helpful to anyone, thank you). Cleaning up your exported SVG
<title> elements should be considered a necessary task in your organization’s workflow.
If an SVG’s code is fully inlined, then unnecessary child elements and content should be optimized away.
paths and any other direct child elements of the SVG should receive an
aria-hidden="true" if they contain no information that should be made accessible.
SVGs that are decorative
A SVG that does not add important information to a document should be considered decorative. The following markup pattern uses
aria-hidden="true" to hide the SVG from screen readers.
focusable="false" is also used to ensure Internet Explorer won’t allow the Tab key to navigate into the SVG.
<svg aria-hidden="true" focusable="false"> <!-- ... --> </svg>
Since the SVG is meant to be hidden, there’s no need to add a
role attribute, as it would be ignored anyway.
Note that while some screen readers may ignore an SVG if it has no
role or accessible name, other screen readers may still find the element and announce it as a “group” without an accessible name. It’s best to avoid these situations by always using
aria-hidden="true" if the SVG is meant to be decorative.
SVGs within links and buttons
When attempting to use the SVG patterns that had the fewest issues, to provide links and buttons with their accessible names, it became clear that what worked outside of interactive elements would not necessarily work within.
Take for example a pattern that performed very well as stand alone content, with exceptions noted only for JAWS paired with Internet Explorer 11 (exceptions noted in test results, linked to at the end of this article):
<a href="#!"> <svg role="img" aria-label="Accessible Name" focusable="false"> <use xlink:href="#..." aria-hidden="true"></use> </svg> </a>
However, when using this same pattern within a link or button, different gaps and failures occurred. More troubling, was that the gaps were different depending on if the pattern was a child of a button or link.
Using the SVG within a button
The noted SVG pattern passed all tests, except when using Microsoft Edge. With Edge, the SVG failed to provide a
button with an accessible name, regardless of screen reader pairing. Remember that bug I mentioned about Edge not recognizing an image element’s
alt value? Here we are again…
The bright side(?) here is that when Chromium-Edge is publicly released, this failure will likely go away. When that happens, this particular pattern would have full support across all browsers and screen readers tested.
Using the SVG within a link
Unfortunately, SVGs within links fared a bit worse. Unlike the button example which was problematic only with current Edge, SVGs within links do not communicate accessible names with IE 11 paired with NVDA, or current Edge paired with JAWS.
Additionally, the pattern resulted in JAWS not being able to navigate to the link/SVG if paired with IE11 or Chrome.
SVG link and button patterns with no gaps
To ensure that links and buttons containing SVGs will be accessible, regardless of the screen reader and browser pairing used, it may be best to provide accessible names via other means, and treat the SVG as decorative.
A consistent SVG markup pattern to consistently provide links and buttons an accessible name is far superior than implementing a pattern with gaps.
For example, the following would use the element’s visible text to provide the accessible name:
<button> <svg focusable="false" aria-hidden="true"><!--...--></svg> Search </button> <a href="/search"> <svg focusable="false" aria-hidden="true"><!--...--></svg> Search </a>
If the elements’ intended designs called for using the graphics alone, visually hidden text could be used:
<button> <svg focusable="false" aria-hidden="true"><!--...--></svg> <span class="sr-only">Search</span> </button> <a href="/search"> <svg focusable="false" aria-hidden="true"><!--...--></svg> <span class="sr-only">Search</span> </a>
aria-label set to the link or button element would achieve the same effect (and btw,
aria-label should be translatable now with Google Translate!):
<button aria-label="Search"> <svg focusable="false" aria-hidden="true"><!--...--></svg> </button> <a href="/search" aria-label="Search"> <svg focusable="false" aria-hidden="true"><!--...--></svg> </a>
For more detailed information about providing accessible names to interactive elements when using SVGs, I’d recommend reading Sara Soueidan’s post, Accessible Icon Buttons, as well as the links mentioned in the further reading section.
I started writing this article and performing these tests at the end of 2018. Clearly, I took some time to finish them all up. But the reason I mention this is because during that time span there were many releases to browsers and screen readers alike. The time gap was actually favorable to some tests. And as I’ve noted already, Edge is on the cusp of being re-released with a completely different engine under the hood, results are about to get more… uniform at least.
It is worth noting that these test results, and the patterns I’ve identified as providing the most robust support, are representative of a snapshot in time. The advice I will give time and time again is to test the markup patterns you’re using and ensure the intended information is being communicated to all users.
I’m looking forward to writing future updates to this article, and noting any fixed bugs, and updated support for the markup patterns. But for now, here are links to the tests and results these findings were based on:
- Static Image and SVG test results
- Images and SVGs providing accessible name to links test results
- Images and SVGs providing accessible name to buttons test results
As I mentioned at the beginning of this article, here are some links to additional resources to check out. Many of them go into far greater / different details about similar patterns, and why providing accessible names to graphics (and their parent elements) is so important:
- Accessible SVGs, by Heather Migliorisi (2016)
- Accessible Icon Buttons, by Sara Soueidan (2019)
- Deque: Creating Accessible SVGs, by Carie Fisher (2019)
- An Alt Text Primer, by Nicolas Steenhout (2019)
- WAI Tutorials: An
altDecision Tree, (2017)
- 24 A11y: Accessible SVG Icons with Inline Sprites, by Marco Hengstenberg (2018)
- Accessible SVG flowcharts, by Léonie Watson (2018)
- SVG Icons and Screen Reader Accessibility (slideshare), by Dennis Lembree (2019)
- Short note on use of
titleattribute, by Steve Faulkner (2016)
- Accessible Name computation for
<img>, W3C HTML AAM
- Understanding WCAG 1.1.1: Non-text Content
- Bug: SVGs not properly reporting their accessible names in Firefox, reported in 2017
Some shout outs
Thank you to Steve Faulkner for reminding me about child elements of buttons inheriting
role=presentation, to Patrick H. Lauke for test result feedback, Eric Bailey for reviewing this article, and to Sara Soueidan for encouraging me to finish the work I had started. This article would have likely continued to sit on the back burner without all of you.