Introduction
The other day I was thinking about inline svgs and (vs?) font icons. Mostly because I'm kind of a nerd. Anyway... We use a fair amount of font icons at work. They're sometimes useful, but I also know that they have issues.
- If font icons fail, you get ugly squares all over your site.
- They can (sometimes) have a negative impact on accessibility.
- They are (often) harder to position on a page since you pretty much have to rely on pseudo elements to use them. BTW I really like pseudo elements so nothing against them specifically
- etc etc
So I wanted to learn more about inline svgs. But at the same time, I wanted to keep my markup clean and dumping a ton of svg markup into my code seemed like the wrong way to go.
The <use>
tag
Fortunately, svg's can take advantage of the <use>
tag. This tag can take an href
property which allows you to point the tag at a resource.
Reference an svg on the page
To reference an svn the same html document, you can use the <use>
tag like this.
- add an svg with an id to the page
- add another svg with a
<use>
tag inside it - point the
href
property at the correct id
<!-- index.html -->
<!-- declare an svg -->
<svg id="my-svg">
<!-- the markup for the svg -->
</svg>
<!-- use an svg by reference -->
<svg>
<use href="#my-svg" />
</svg>
That's great! But sort only half the problem. I've just moved the svg markup around on the page. I still potentially have to scroll through (very often) a lot of svg markup when I'm working on a project.
Reference an svg from a folder
What's nice though is to store these svgs as assets somewhere else. For instance what if I had an assets directory and in that I put the svgs. Can I get the same effect as the first example? Yes!
<!-- /assets/my-svg.svg -->
<!-- declare an svg in a directory -->
<svg id="my-svg">
<!-- the markup for the svg -->
</svg>
<!-- index.html -->
<!-- use an svg by reference -->
<svg>
<use href="/assets/my-svg.svg#my-svg" />
</svg>
One important thing to note is that you still have to point the tag at the id of the svg. Otherwise, it doesn't know what to do.
Reference from a CDN?
Ok so (I thought) can I take this a step further? Will the href
of the <use>
tag work the same way as a link or like src
for an image? Well... no. Also yes. Let me explain.
You can't quite do it directly because (I'm pretty sure) of content security policies. If that's wrong, feel free to let me know. So you can't do something like this
<svg>
<use href="https://my-cdn.com/my-svg.svg#my-svg" />
</svg>
So what's a developer to do? Use gulp!
Gulp
If you're unfamiliary with gulp it allows you to create your own tasks which you can use in projects. This is not a tutorial on gulp per se. I'm going to assume some prior knowledge here. But, you can do all kinds of things with gulp like:
- minify and concatenate code
- manage images
- turn sass files into css files
- turn modern javascript into "older" javascript
- etc
I thought what if I could write a task that downloads the assets I want and puts them in a directory. That would give me a bunch of advantages.
- A single source of truth for the svgs. Designers will like this because it means we can update all our icons at once.
- We don't have to keep the svgs in version control. We can
.gitignore
svgs just like we do with other kinds of built assets. - We can use the same approach with our CI/CD process to update all sites at once. Developers will like it because it will mean we don't have to hunt down a ton of icons.
Best, it turns out to be a straightforward process. To write the appropriate task, you really only need one dependency (besides gulp). The gulp-download2
package available from npm. This package does what it says and lets you download things for use in tasks. Using gulp 4, you can write a download
task like this.
// gulpfile.js
const gulp = require('gulp')
const download = require('gulp-download2')
// either a string for one url or an array of strings
const urls = [
`https://my-cdn.com/shared/images/my-svg.svg`,
`https://my-cdn.com/shared/images/another-svg.svg`
]
const dl = () => {
return download(urls)
.pipe(gulp.dest('icons'))
}
gulp.task('download', gulp.series(dl))
When you run gulp download
this task will place my-svg.svg
and another-svg.svg
into the /icons
directory in the current project. It'll even make the directory if it doesn't exist.
Internet Explorer!?
So there is a downside. IE doesn't support referencing svgs outside the current document. That means that it can't natively use the second approach. So if you're (still) supporting IE you need a little extra work. And before you ask, yes we still support IE because governments ~~often~~ sometimes move very slowly.
Fortunately you can use a library such as svg4everybody. To use it, you need to include the svg4everybody
script somehow in your project and then call the function. This is an example.
<script src="path/to/svg4everybody.js"></script>
<script>svg4everybody()</script>
Now the svgs will be available across an even wider range of browsers.
Conclusion
I think this is a really nice way to get a lot of your svgs stored consistenly outside your projects. I like that it potentially improves accessibility and consistency in production. It's also really nice that as a developer experience it's straightforward to understand.