SVG Sprites for HTML and CSS
SVG sprites are a great way to serve icons. They have been around for quite some time.
The basic approach:
- Add each icon as a
<symbol>
to a singlesprite-symbols.svg
file:<svg xmlns="http://www.w3.org/2000/svg"> <symbol viewBox="0 -960 960 960" id="search"> <path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z" /> </symbol> <symbol viewBox="0 -960 960 960" id="close"> <path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" /> </symbol> </svg>
- Render SVG with
use
element. Reference specific symbol via fragment identifier:<svg class="icon"> <use xlink:href="sprite-symbols.svg#search" /> </svg>
- Set color via CSS:
.icon { fill: currentColor; }
The result:
How to reference the sprite in CSS?
What if we are unable to control the HTML and want to render the icon via CSS instead?
Let’s add a background image:
<style>
.var-background::before {
background-image: url(sprite-symbols.svg#search);
/* ... */
}
</style>
<button type="button" class="icon-button var-background">
Search
</button>
Unfortunately, the icon is not rendered:
For this to work, we need to expose a <view>
element in the sprite. Fortunately, it can be combined with our existing <symbol>
via a <use>
intermediate:
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<symbol viewBox="0 -960 960 960" id="search">
<path
d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"
/>
</symbol>
<use xlink:href="#search" width="960" height="960" x="0" y="0"></use>
<view id="search-view" viewBox="0 0 960 960" />
<symbol viewBox="0 -960 960 960" id="close">
<path
d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"
/>
</symbol>
<use xlink:href="#close" width="960" height="960" x="0" y="960"></use>
<view id="close-view" viewBox="0 960 960 960" />
</svg>
Now we can use the fragment identifier of the new <view id="search-view" />
element:
.var-view::before {
background-image: url(sprite-hybrid.svg#search-view);
/* ... */
}
The updated result:
How to change color on hover/active?
mask-image
to the rescue. Instead of using background-image
, we set a background color and use the icon as a mask:
.var-mask::before {
background: currentColor;
mask-image: url(sprite-hybrid.svg#search-view);
/** ... */
}
The final result:
How to automate this?
I’m currently using the highly recommended svg-sprite library to generate SVG sprites from a folder of icons. At the time of writing this article, it does not yet provide a hybrid mode. However, since its compile method returns both the generated SVG and all shapes, we can use the symbol
output mode and manually extend the generated SVG with a <use ... /><view ... />
for each symbol by looping through the shapes.