Pitfalls of replacing an icon font with svg icons
This blog uses a fair few icons, mostly for decorative reasons, such as the icons in the menu. Until today, these had been placed using a font kit from Font Awesome, which worked by using JavaScript to replace tags in the form of <i class="fa-…"></i> with the appropriate icon from their library.
However, for some time I’d been wanting to move away from this method because hosting stuff away from your site is a privacy concern; at the very least, the IP address of the visitor will be passed onto a third party, but in the case of Font Awesome’s kits, they also collect information like how many times kits have been used etc.. Also, I just wanted more flexibility than you get limiting yourself to one source of icons.
The obvious choice to replace an icon font is to move to using svg icons, especially when you don’t have too many icons in the site, as is the case here. However, doing this hasn’t been quite as straightforward as I expected. Below records some whys and wherefores of the process.
How to place svg icons
The simplest, and perhaps the most flexible, method to add an svg icon to html is simply place your svg code inline, i.e. directly into the html inside <svg> … </svg> tags.
This method allows full css access to the paths within the svg itself, and hence you can do all sorts of fancy things, such as animate your icons, really quite easily.
However, there’s an issue just doing this too - precisely that your svg code appears in your html code. If you are ever viewing the actual html source, then it’s cluttered with an awful lot of content from svgs.
Hence, James and I are using two other methods for icon svgs:
- ‘Cloning’ from an external svg sprite file
- Assigning with css on pseudo-elements
Svgs from an external svg sprite file
This method involves placing all your svg icons in a single text file and calling the the specific one you want via its id #identifier with the <use> attribute.
Thus, you have an external file ‘icon.svg’ of the form:
<svg xmlns="http://www.w3.org/2000/svg">
<!-- svgs from Feather Icons (https://feathericons.com; MIT License) -->
<symbol id="arrow-right" role="img" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-right"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline>
</symbol>
<symbol id="arrow-left" role="img" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-left"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline>
</symbol>
<symbol id="layers" role="img" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layers"><polygon points="12 2 2 7 12 12 22 7 12 2"></polygon><polyline points="2 17 12 22 22 17"></polyline><polyline points="2 12 12 17 22 12"></polyline></symbol>
...
</svg>
and then in the html, for e.g. a right arrow:
<svg class="my-icon-class">
<use href="/path/to/icon.svg#arrow-right"/>
</svg>
You are very limited on what can be styled via the css my-icon-class (this is because cloning results in a non-exposed DOM tree), but that’s fine for many simple icons.
Svgs with css on pseudo-elements
We also use svg icons on pseudo-elements, which are inserted via the css. In particular, the triangular caret bullet for unordered lists in posts is inserted as a background-image on its ::before pseudo-element, and moved around to where it looks nice by playing with the margins (I have no issues using negative margins for positioning).
ul li::before {
content: '';
background-image: url('caret-right-solid.svg');
background-repeat: no-repeat;
height: 1.1em;
width: 1.1em;
position: absolute;
margin: 0.35em 0 0 -1.6em;
}
This method doesn’t allow you to change your svg at all with css (e.g. for colour), so you’re limited to doing that within the svg itself.
Ensuring accessibility
One of the advantages claimed of svgs vs icon fonts is that accessibility can be built into svgs themselves, as svgs can have child elements, especially <title>, which can be used to pass information to assistive technology, especially screen readers.
However, many, if not most, uses of icons are in fact just decorative, and for those there is no issue as they just should be hidden from screen readers with aria-hidden="true". Background images, by definition, will also be hidden, and are always decorative in this blog.
Pertinently, however, we are also using icons in the prose of the posts, as you would emojis, and those do have semantic meaning that can change depending on context. Or, for instance, it’s actually an array of five separate icons that makes an image with four filled stars and then one empty star - and collectively that’s not ‘filled star’ times four followed by ‘empty star’, but ‘four out of five stars’, yet maybe in another case the icon star should indeed be read as ‘filled star’ or just ‘star’. In yet other cases, the star could only be decorative. Thus, you need to provide a descriptive text that is modifiable for each use.
Hence, to completely hide decorative icons, yet implement a description easily within the content written in markdown and processed into html by Hugo (i.e. in the posts themselves; see setting up the blog), we decided to keep it simple, and add aria-hidden="true" to all icons and to build in screen reader text into the shortcode that calls icons for when that is necessary. This, however, does mean screen readers don’t have the context of whether something is an image or text, so is perhaps something to re-adjust at some point.
At the moment, the final Hugo shortcode for our blog post content icons is:
{{- $icon := .Get 0 -}}
{{- $screenreader_text := .Get 1 -}}
<svg aria-hidden="true" class="content-icon"><use href="{{ "icons.svg" |absURL}}#{{ $icon }}"></use></svg>
{{- with $screenreader_text -}} <span class="sr-only"> {{ . }} </span> {{- end -}}
This is called with {{< svg_icon identifier "screen reader text" >}} in the markdown.
Unexpected problems and our solutions
Sizing
When using icons next to prose, emoji style, svg icons turned out to be far more complicated to place than an icon from a font. The only way to do this is was once again by trial and error fiddling with the size and vertical-align until it looked right.
For icons in prose here, I went with css to make them a little bigger than the font size and pull them down by 0.25em, and even now I wouldn’t say this is completely successful as some icons really shouldn’t have a square aspect ratio, a definite disadvantage over a font. Your mileage might vary.
.content-icon {
display: inline-block;
width: 1.15em;
height: 1.15em;
vertical-align: -0.25em;
}
Using with punctuation
So, here I am with sized svg icons in my prose. But ahhhh… the icons are no longer treated as text as they were when they were a font, and that means they don’t stick normally with punctuation.
If one of your visitors has just the wrong browser window size, then you get some very odd looking errors as lines start or end with punctuation.
A bit more hunting around for the html equivalent of the print typesetting instruction ‘do not split’, and I discovered what can work here is surrounding your bits of text with a span styled as display: inline-block, as browsers tend to keep these elements together.
I’m rather glad it doesn’t happen too often, as I’m adding this into the markdown these posts are written in by hand - the html isn’t really any longer than a Hugo shortcode - but it might be something to look at using javascript for.
Svg icons and overflow-x
Finally, a rather specific issue.
In a previous blog post, we have a table where there’s a line of stars as a rating of some cross-country races. This table does not fully fit on mobile, and to account for that we’d added a wrapper div around the table with overflow-x: auto; css to make sure a horizontal scroll bar appeared when the table was too wide for the viewport.
Well, that certainly needed some playing with to get to work.
First, within the table cell itself, we needed an extra css wrapper div with the style width: max-content; around the array of separate icons to prevent them cascading vertically, shrinking or overlapping when the table cells weren’t wide enough to fit them. Then, all looked well… until I checked it on the mobile version.
Ahhhhh… an unwanted horizontal scroll on the entire page and not just the table inside the wrapper. It took forever, but eventually I stumbled on one line of css to add to the overall wrapper div to fix that: position: relative;.
I can only hope nothing too odd is happening in a (modern) browser I don’t test in person; I honestly doubt I’d have spotted the issue in an emulator. Although I have seen unwanted horizontal scrolls on plenty of websites that do have the benefit of a professional front-end developer, so maybe this is just tricky.
In conclusion…
Using svg icons, especially within prose, is more complicated than using an icon font kit, and requires some work.
But doing so means I’m not having to self-host a large font with lots of icons I’d never use or rely on a external icon source, and hopefully having sorted these first few pitfalls, it will get simpler here on in.
Extra shots
- Iconduck resource for freely licensed svg icons
https://iconduck.com - Font Awesome on font icons vs svg icons (note, they provide both) (2021)
https://blog.fontawesome.com/webfont-vs-svg/ - Michael Irigoyen on on icon fonts vs svg icons (2021)
https://www.irigoyen.dev/blog/2021/02/17/stop-using-icon-fonts/ - Chen Hui Jing on the methods for inserting svg icons (2021)
https://chenhuijing.com/blog/the-many-methods-for-using-svg-icons/ - More on accessible svg icons (2021)
https://css-tricks.com/accessible-svg-icons/ - More on accessibility for svgs in general (2021)
https://www.smashingmagazine.com/2021/05/accessible-svg-patterns-comparison/ - Elliot Dahl on aligning svg icons with text (2017)
https://blog.prototypr.io/align-svg-icons-to-text-and-say-goodbye-to-font-icons-d44b3d7b26b4 - A fancier way to do star ratings with svgs and css (2021)
https://samuelkraft.com/blog/fractional-svg-stars-css
Credits
Thank you to those providing the open source icons used here. All icons in this blog are used under an appropriate licence, with further credits details given in svg source files and in the site credits.
- Photo of creating icons by Balázs Kétyi on Unsplash
Comments (0)
No comments yet.