Vector icons for web-apps can be implemented using SVG inlined into CSS with data URLs. This can improve performance vs using separate .svg files by avoiding extra network requests, and can be a simple, zero-dependency alternative to font-based or template-based icon insertion.
Encoding
As serveral articles state (Stack Overflow, CSS-Tricks), certain characters must be URL-escaped and Base64 encoding is sometimes suggested as a workaround. For the payload of a data-URL however, only a subset of the usual characters reserved for URLs must be escaped. In many cases, escaping can be avoided altogether:
Use single-quotes for the surrounding url('') so double-quotes can be used for SVG tag attributes.
Use rgb() or color names like white or black instead of hex values to avoid the '#' character, which would otherwise need to be percent-encoded.
CSS supports multi-line strings by appending a line continuation character ('\') to each line. This can allow for nicely indented SVG snippets. With the formatting kept more readable like this, it can be practical to type out SVG syntax for simple graphics directly into the stylesheet.
If you can accept the default character encoding for data URLs (US-ASCII), you can omit an encoding declaration like ;utf8, thus further reduding the boilerplate for each graphic. For the limited use-case of simple paths and lines, the default character encoding is enough.
Note that the SVG xmlns attribute is required, but the version attribute is ignored by browsers and can be omitted.
Examples
These data URLs can be pasted directly into your CSS as a background-image
When creating more complex graphics or importing icons, I use a combination of Inkscape, SVGOMG and manual text editing to produce clean SVG markup suitable for inlining into CSS. Here's an example workflow using the Blender logo from Wikipedia:
Browser support
The following stress-test uses various SVG elements and color encodings, and was applied to a range of browser versions using BrowserStack:
Full support
Partial support
No support
Chrome
8070656460504037
Safari
1312.111.110.19.187.16.265.1
5 (1)
4
Firefox
7574706652514532
Edge
8180181716
15 (2)
IE
(none)
11 (2) 10 (2) 9 (2)
876
(1)
Translucently isn't handled correctly
(2)
Must be fully URL-encoded
Every major browser rendered the stress-test correctly, except IE: it requires full URL-encoding in the data URL. Given this, I'd say unencoded inline SVG is suitable for web apps that already require a modern browser, but not suitable where maximum compatiblity is a requirement, like a government website.
Compared to JS-based SVG insertion
I've seen several Javascript-based methods of inlining vector icons into web apps, such as defining custom helpers for JS templating systems or using a React component to re-use SVG <symbol> elements. Icon insertion methods that use Javascript to create 'real' SVG DOM elements can have disadvantages though:
Reduced accesibility and SEO performance due to non-semantic SVG elements in DOM.
Obscures more important elements when inspecting/debugging live DOM.
The same SVGs can end up being parsed multiple times by the browser if the icon is reused on multiple elements.
Reduces HTML renderer performance by increasing the number of elements considered for CSS selection, hit-testing etc.
Reduces javascript performance with extra interaction with the DOM API.
Defining icons in pure CSS can allow for clean, semantically-correct HTML that's pleasant to inspect/debug, and minimizes performance bottlenecks.
Compared to font-based icons
Font Awesome, for example, provides a multitude of icons packaged as web fonts. It's easy to integrate and there's a wealth of customizations, but there's still a few disadvantages vs this pure-CSS solution:
Separate network request for font
Not easily edited
Requires pseudo-elements to contain the special characters
Gotchas and limitations
Color themes can't be applied.
No error feedback for incorrect SVG syntax or improperly escaped data URLs