Accessible SVG: Patterns That Work with Screen Readers
Adding <title> to an SVG is not the same as making it accessible. Which markup you need depends on what the graphic is doing on the page — carrying information, decorating text that already says the same thing, or acting as a button — and those three cases want three different answers. Two of them involve adding nothing at all to the drawing.
1. Decide what the graphic is for, first
Every good guide on this subject starts with the same fork, because nothing downstream makes sense without it:
- Decorative. The graphic repeats or ornaments adjacent text. A star beside the word "Favourites". Announcing it is noise, so the correct treatment is to hide it.
- Informative. The graphic carries meaning nothing else on the page carries — a status icon, a chart, a diagram. It needs a text alternative.
- Interactive. The graphic is inside a link or a button. The thing that needs a name is the control, not the picture.
Most icons in a typical interface are decorative, which is why indiscriminately titling everything makes a page worse rather than better: a screen reader user hears "star, star, star" between the labels they were trying to read.
2. Decorative: hide it, and hide it completely
<!-- A graphic that repeats what the text already says
should be silent. Two attributes, both needed. -->
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path d="M12 2l3 7h7l-6 4 2 7-6-4z" />
</svg>
<span>Favourites</span>
<!-- aria-hidden removes it from the accessibility tree.
focusable="false" stops older engines handing it a
tab stop, which produced a focusable element that
announced nothing at all. -->aria-hidden="true" removes the element from the accessibility tree. focusable="false" handles a separate, older problem: some engines treated SVG as focusable, so a hidden graphic could still receive a tab stop and present the keyboard user with a focused element that announced nothing. Modern browsers no longer do this, but the attribute is one word and it costs nothing to keep.
3. Informative: a role and a title
For a graphic that means something, the pattern that holds up best across real combinations of browser and screen reader is also the simplest one: role="img" on the root, and a <title> as its first child.
<!-- The simplest pattern that survives real screen
reader testing: an explicit role, and a title as
the first child of the svg. -->
<svg viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg">
<title>Delivery delayed</title>
<path d="M12 2l3 7h7l-6 4 2 7-6-4z" fill="#f97316" />
</svg>
<!-- role="img" is not redundant. It collapses the
children into one object, so the reader announces
the title instead of walking the shapes. -->The role is doing real work. Without it the reader may descend into the shapes and narrate a group of paths; with it, the SVG is one object with one name. Note also that <title> is not the HTML title attribute — it is an element, it must be the first child to be reliably picked up, and browsers happen to show it as a tooltip, which is a side effect rather than its job.
4. When a name is not enough
A chart needs a description as well as a name, and this is where the obvious markup turns out to be the unreliable markup. Adding <desc> next to <title> and hoping both are read is the combination that testing finds patchy — some readers announce one and ignore the other. Giving both elements ids and pointing aria-labelledby at both is the version that survives.
<!-- A chart or diagram needs more than a name. Give
both elements ids and point aria-labelledby at
both — title and desc alone is the combination
testing finds unreliable. -->
<svg viewBox="0 0 400 300" role="img"
aria-labelledby="chartTitle chartDesc"
xmlns="http://www.w3.org/2000/svg">
<title id="chartTitle">Revenue by quarter, 2026</title>
<desc id="chartDesc">Revenue rose from 1.2M in Q1 to
2.8M in Q4, with the steepest rise between Q2 and
Q3.</desc>
<!-- bars -->
</svg>It is slightly redundant — the text can be announced twice — but nothing gets silently dropped, which is the better failure mode. For a genuinely complex diagram, consider putting the long description in visible page text instead: everyone benefits from a chart that says what it shows, and sighted users cannot read a <desc> either.
5. Icons inside buttons and links
This is the case most often got wrong, and the fix is counter-intuitive: the graphic should be silent. What the user needs is the purpose of the control, not a description of the artwork.
<!-- Wrong: the graphic is named, the control is not.
A reader announces "button" with no purpose. -->
<button>
<svg role="img"><title>Bin</title>...</svg>
</button>
<!-- Right: name the control for what it does, and
silence the graphic entirely. The user needs the
action, not a description of the picture. -->
<button aria-label="Delete draft">
<svg aria-hidden="true" focusable="false">...</svg>
</button>
<!-- Better still, when there is room for it: visible
text needs no aria at all. -->
<button>
<svg aria-hidden="true" focusable="false">...</svg>
Delete draft
</button>Name the button "Delete draft", not "Bin". If the icon is titled and the button is not, a reader announces a button with no purpose, or reads out the shape and leaves the user to guess. And an icon-only control still needs a hit area large enough to use — the drawing being 16 pixels does not mean the button should be.
6. Text inside the drawing is not a text alternative
An SVG <text> element renders words, so it seems like it should serve as the description. In testing it is the least reliable option of all — support for reading it as the accessible name is inconsistent enough that it should not be relied on. Use a <title> even when the words are already visible in the graphic.
A related trap: type that has been converted to outlines is no longer text to anything. It is a set of paths in the shape of letters — unreadable to a screen reader, unsearchable, and untranslatable. If the words matter, they need to exist somewhere that is not a path.
7. Contrast applies to graphics too
Contrast requirements are usually discussed as a text rule, but they cover graphical objects as well: any part of an image needed to understand the content is expected to reach 3:1 against what is behind it. A pale grey icon on white fails, and so does the low-contrast divider between two segments of a chart.
Colour alone is also not enough to carry meaning. A chart whose two lines differ only in hue is unusable to a large number of people — add a dash pattern, a direct label, or distinct markers. Both of these are easier to fix at drawing time than in review.
8. Motion needs an off switch
Animated diagrams and looping decorative graphics can cause real discomfort, and the operating system already knows who has asked to avoid them. The part worth getting right is the resting state.
/* An animated diagram needs a still state, not just
a stopped one — switching the animation off without
saying where things rest leaves a half-drawn frame. */
@media (prefers-reduced-motion: reduce) {
.chart-line {
animation: none;
stroke-dashoffset: 0;
}
}For the full treatment of SVG animation, including where to declare this in a self-contained file, the animation walkthrough covers it.
Where to go next
A note on minifiers, since they are where accessible markup quietly goes missing. An optimiser that treats <desc> as authoring metadata will delete it, and one that strips "unused" ids without reading aria-labelledby will leave the pattern in section 4 pointing at ids that no longer exist. Our optimiser did both until we found it writing this article; it now keeps <title> and <desc> at every setting and counts the ARIA id references as references. It is worth checking whatever you use against the same two cases.
Generated SVG does not arrive with an accessible name either — the model is asked for a drawing, not for a description of one — so the title is yours to add, in the editor or afterwards. Finally, do not trust any of this by reading it. Automated checks catch a missing accessible name and nothing about whether the name is any good; the only real test is turning a screen reader on and listening to the page.
