Published on 17 Sep 2025
Rendering icons as SVG + CSS
SVG is a very powerful format, but it comes at a cost: document size.
Average icon is about 500 bytes, which is not much, but when you have many icons on the same page, page might get rather large, icons can account for more than half of HTML.
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="currentColor" d="M6 17c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1H6m9-9a3 3 0 0 1-3 3a3 3 0 0 1-3-3a3 3 0 0 1 3-3a3 3 0 0 1 3 3M3 5v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2" />
</svg>
Developers have tried to find different ways to render icons that solve HTML bloat.
The most common solutions are:
- Moving icons to CSS, rendering as background or mask images.
- SVG sprites.
CSS solution
CSS solution moves icon to CSS, using simple placeholder element in HTML:
.mdi--account-box {
display: inline-block;
width: 24px;
height: 24px;
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M6 17c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1H6m9-9a3 3 0 0 1-3 3a3 3 0 0 1-3-3a3 3 0 0 1 3-3a3 3 0 0 1 3 3M3 5v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2'/%3E%3C/svg%3E");
background-color: currentColor;
-webkit-mask-image: var(--svg);
mask-image: var(--svg);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
}
<span class="mdi--account-box"></span>
The biggest advantage of using icon in CSS is caching.
CSS files are usually shared between pages and are cached in browser, so browser won't have to reload it on every page.
However, it has big disadvantages:
- Cannot control anything inside icon: stroke width, colors, no css variables, etc...
- Animated icons are broken - timer is started not when placeholder is rendered, but when svg is first rendered.
So if you want to have any control over icon or if icon is animated, you cannot use it in CSS.
Sprites
SVG sprites solution splits icon into 2 parts:
- Sprite, which is a hidden SVG in the same document that contains reusable symbols for each icon.
- Icon, which, instead of including full icon code, only includes
<use />
element referencing symbol in sprite.
<svg width="0" height="0" class="hidden">
<symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" id="mdi--account-box">
<path fill="currentColor" d="M6 17c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1H6m9-9a3 3 0 0 1-3 3a3 3 0 0 1-3-3a3 3 0 0 1 3-3a3 3 0 0 1 3 3M3 5v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2"></path>
</symbol>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<use xlink:href="#mdi--account-box"></use>
</svg>
SVG sprites reduce page size only if you are using the same icon multiple times on page. For icons that do not repeat, it actually makes it worse.
Then you do need to include sprite on every page load. You cannot use external images. It cannot be cached.
SVG + CSS
There is a better solution, which:
- Gives you full control over SVG elements.
- Moves most code to CSS, which can be cached.
- Does not break animated icons.
This solution is SVG + CSS.
It works by moving all attributes of shapes to CSS and assining a unique class name to shape:
.mdi-account-box {
fill: currentColor;
d: path(
'M6 17c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1H6m9-9a3 3 0 0 1-3 3a3 3 0 0 1-3-3a3 3 0 0 1 3-3a3 3 0 0 1 3 3M3 5v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2'
);
}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path class="mdi-account-box" />
</svg>
Now main bulk of icon is in CSS, cached in browser.
You can even use CSS variables to control parts of icon:
.mdi-account-box {
fill: var(--primary-color, currentColor);
d: path(
'M6 17c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1H6m9-9a3 3 0 0 1-3 3a3 3 0 0 1-3-3a3 3 0 0 1 3-3a3 3 0 0 1 3 3M3 5v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2'
);
}
Awesome, isn't it?
Issues
If this is so awesome, why is it not widely used?
It comes down to two issues:
- While
path()
function in CSS existed for a while, it was used mainly for clip paths. Support for usingpath()
ind
property has landed without fuss and was unnoticed. - Safari browser does not support
d
property in CSS.
Safari browser
The biggest issue to using SVG + CSS is lack of support in Safari browser.
It is the main browser on Apple devices and cannot be ignored.
Firefox and Chrome have supported d
property for a while, but Safari is still working on it.
At the moment of writing this article, Safari 26 has just landed. It still does not support d
property.
However, Safari Technology Preview does support it!
When I started experimenting with SVG+CSS back in March 2025, Safari Technology Preview already had support for d
property.
Since then, 2 minor versions and one major version have been released, but feature still didn't make it to a stable release.
Conclusion
I think SVG + CSS is the future of icons.
It is way better than SVG sprites.
It lets developers use CSS variables to control parts of icons: palette, stroke width.
It lets developers animate icons with CSS!
d attribute
By far the biggest part of SVG is d
attribute of <path />
elements.
Without support for d
attribute in CSS, moving other attributes to CSS doesn't make much sense.
So lack of support in Safari browser is a huge let down.
Workaround
I'm working on a workaround for Safari support and early test version is actually used on this website for footer icons.
Hoping to release it soon as a part of Iconify project.
It relies on Iconify API to render full icons in Safari browser, rendering SVG+CSS in other browsers and SSR.