Skip to content

Instantly share code, notes, and snippets.

@davidhund
Last active March 27, 2022 12:18
Show Gist options
  • Save davidhund/564331193e1085208d7e to your computer and use it in GitHub Desktop.
Save davidhund/564331193e1085208d7e to your computer and use it in GitHub Desktop.
Figuring out the most robust, accessible markup for SVG Sprite Icons

(as a reply to: https://css-tricks.com/svg-use-with-external-reference-take-2/)

While I love SVG (sprites) there are a lot of issues to take into account.

Advocating The Most Accessible Markup

UPDATE: you should take a look at https://dl.dropboxusercontent.com/u/145744/accessible-svg-icon/test.html which seems a simpler and more robust approach

Another thing: since people copy-paste our code examples it would be great if we could advocate the most robust and accessible markup IMO. I am no a11y expert, but in my understanding we could/should take some extra steps to make out SVG sprite icons more accessible.

Below my current thinking, I would love an a11y expert's advice on this!

‘Presentational’ or ‘Content’ icons?

First: make a distinction between 'presentational' and 'content' icons. Just as we would with img and their alt attributes.

For all SVG Icon sprites (in sprite.svg):

  • Add a title element child to the sprite's symbol
  • Add a desc element child to the sprite's symbol
<symbol id="left-arrow">
	<title>Back</title>
	<desc>An arrow pointing left</desc>
	<path etc.../>
</symbol>

Presentational use

Now, for useing those symbols as purely presentational icons:

  • Use role="presentation" on the SVG
<svg role="presentation">
    <use xlink:href="sprite.svg#left-arrow"/>
</svg>

That's it. But for SVG icons as 'content' we should do more:

Content use

  • Add role="img"
  • Add a title attribute
  • Add a title element with unique id
  • Add aria-labelledby="title-id"
<svg role="img" title="Back" aria-labelledby="title-back1">
	<title id="title-back1">Back</title>
    <use xlink:href="sprite.svg#left-arrow"/>
</svg>

This ensures the best accessibility AFAIK. Again: I am no a11y expert, so please correct me when I'm mistaken.

Thoughts?

Source information:

@davidhund
Copy link
Author

So @fvsch the following would be a good summary, so far?
(My main doubt is re: span[aria-label] in the 2nd example…)

Standalone Icons as decoration

<svg aria-hidden="true">
  <use xlink:href="#symbol-id"></use>
</svg>

✔ VoiceOver | ✔ NVDA | ✔ JAWS 16

Standalone Icons as content

<svg role="img" aria-label="Text alternative">
  <use xlink:href="#symbol-id"></use>
</svg>

✔ VoiceOver | ✔ NVDA (label read twice) | ✔ JAWS 16

Note: the aria-label is read twice in NVDA/Fx.
The label on a parent element, however, has issues elsewhere (?)

<span aria-label="Text alternative">
  <svg aria-hidden="true">
    <use xlink:href="#symbol-id"></use>
  </svg>
</span>

✖ VoiceOver | ✔ NVDA | ✖ JAWS 16

Standalone Icons as interactive content

(Examples work with A or BUTTON)

<a href="#" aria-label="Text alternative">
  <svg aria-hidden="true">
    <use xlink:href="#symbol-id"></use>
  </svg>
</a>

✔ VoiceOver | ✔ NVDA | ✔ JAWS 16 / Fx40 + IE11

The following makes more sense but has issues with JAWS:

<a href="#">
  <svg role="img" aria-label="Text alternative">
    <use xlink:href="#symbol-id"></use>
  </svg>
</a>

✔ VoiceOver | ✔ NVDA | ✔ JAWS 16 / Fx40 | ( ✖ JAWS 16 / IE11 in button)

@fvsch
Copy link

fvsch commented Oct 2, 2015

Your summary is almost right, but the "Standalone Icons as content" is actually more tricky.

In JAWS (JAWS16+IE11), <svg role="img" aria-label="Text alternative"> outside of an interactive element is not read. aria-label on a span is not read either. The only thing in my test page that works for vocalizing an illustration icon was this:

<span title="">
  <svg aria-hidden="true"></svg>
</span>

So if we want to cover JAWS, we might try this:

<span title="The label">
  <svg role="img" aria-label="The label"></svg>
</span>

We'd have to test this solution specifically, because right now in the test page I'm not testing this belts-and-braces markup. Does it work reliably in JAWS and other screen readers? Does it work both with <svg><use/></svg> and with <svg><!-- many paths here --></svg>?

@fvsch
Copy link

fvsch commented Oct 2, 2015

If we didn't have to support JAWS we could just recommend this:

Hide a decorative icon from screen readers

<svg aria-hidden="true"></svg>

✔ VoiceOver+Safari | ✔ NVDA+Fx | ✔ JAWS16+IE11

Provide accessible text for an icon

<svg role="img" aria-label="Text alternative"></svg>

✔ VoiceOver+Safari | ✔ NVDA+Fx (label read twice) | ✖ JAWS16+IE11 (works in links only)

@fvsch
Copy link

fvsch commented Oct 2, 2015

UPDATE!!!1

Turns out I had ommitted to test <title> elements inside the HTML page's <svg> icons! Aaaand they work wonders. Updated test page: https://dl.dropboxusercontent.com/u/145744/accessible-svg-icon/test.html

Results:

  • Perfect in JAWS16+IE11
  • Alright in NVDA (still saying the text twice)
  • Works in VoiceOver (requires role="img" for images outside of interactive elements)

Hide a decorative icon from screen readers

<svg aria-hidden="true">
  <use xlink:href="sprite.svg#my-icon"></use>
</svg>

✔ VoiceOver+Safari | ✔ NVDA+Fx | ✔ JAWS16+IE11

Provide accessible text for an icon

<svg role="img">
  <title>Text alternative</title>
  <use xlink:href="sprite.svg#my-icon"></use>
</svg>

✔ VoiceOver+Safari | ✔ NVDA+Fx (text is read twice) | ✔ JAWS16+IE11

Note: the role="img" is only required by VoiceOver for reading icons outside of buttons or links. We recommend using it, to be on the safe side.

@davidhund
Copy link
Author

Thanks for your work @fvsch, really great to have test-results.
So we don't need aria-labelledby for the title elements? That's great (less authoring).

Your last snippet (title el in svg), does that work for icons in interactive els (A, BUTTON)? Or do those still need an aria-label?

@fvsch
Copy link

fvsch commented Oct 3, 2015

does that work for icons in interactive els

Yep, it works in interactive elements and in general flow, no need for aria-label (I do like the aria-label approach a tiny bit better, but support is more limited).

For the record I reached out to @chriscoyier to offer writing a guest post on CSS-Tricks. Since they have published a handful of pieces on SVG sprites (I'm linking to them in our internal documentation at work, for instance), that seems like a good place to publish a "definitive (as of 2015)" mini-guide.

@davidhund
Copy link
Author

Hey @fvsch — that's a good idea and exactly the reason I started looking into this topic in the first place ;-)

It would be great to use/advocate the most accessible markup for SVG Sprites and a high profile platform such as CSS Tricks would help a lot.

@anil1687
Copy link

Hi @fvsch,

I'm facing same issue regarding NVDA+FF reading text twice. Do we have any alternative for this.

@francishogue
Copy link

francishogue commented Oct 5, 2016

Can anyone make that test link from dropbox accessible again? (https://dl.dropboxusercontent.com/u/145744/accessible-svg-icon/test.html). Thank you!

@alexvb
Copy link

alexvb commented Oct 30, 2016

@fvsch It seems that the test page has gone. Could you make available again?

@davidhund
Copy link
Author

Unfortunately the tests have gone from @fvsch his Dropbox pages, for implementation I generally refer to his excellent guide at: https://fvsch.com/code/svg-icons/how-to/#section-adding

@md-azam12
Copy link

md-azam12 commented Jun 12, 2017

try this-->

<svg role="img" aria-labelledby="uniqueId_title">
      ....
    <title id="uniqueId_title">ThumpUp song</title>
</svg>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment