Embedding SVG Icons in CSS
Tags: svg, css, showdev
Published: 2023-09-18 06:06:30

I can't quite recall what sparked the idea to embed all SVG icons in CSS instead of importing them as JSX components. Perhaps I was struggling to manage SVG assets for several small projects at the time. Regardless, this exploration delves into embedding SVG icons in CSS.

We're all aware that [Probably don't Base64 SVG](https://css-tricks.com/probably-dont-base64-svg/), but using URL-encoding seems to be a viable alternative. Normally, we utilize the `background-image: url(URL_ENCODED_SVG);` format. However, it's challenging to modify the SVG color using this method. _Thankfully, the CSS mask feature allows us to transform the SVG into a mask and change its background color, granting us the ability to alter the SVG icon color._

To demonstrate how this works, let's use the [Entypo home icon](https://github.com/hypermodules/entypo/blob/master/src/Entypo/home.svg?short_path=63e8a4e) as an example. Here's the SVG source code:

```svg
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Home" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
	 viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
<path d="M18.672,11H17v6c0,0.445-0.194,1-1,1h-4v-6H8v6H4c-0.806,0-1-0.555-1-1v-6H1.328c-0.598,0-0.47-0.324-0.06-0.748L9.292,2.22
	C9.487,2.018,9.743,1.918,10,1.908c0.257,0.01,0.513,0.109,0.708,0.312l8.023,8.031C19.142,10.676,19.27,11,18.672,11z"/>
</svg>
```

We can convert this icon to the URL-encoded version using the [URL-encoder for SVG](https://yoksel.github.io/url-encoder/).

```css
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3C!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3E%3Csvg version='1.1' id='Home' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 20 20' enable-background='new 0 0 20 20' xml:space='preserve'%3E%3Cpath d='M18.672,11H17v6c0,0.445-0.194,1-1,1h-4v-6H8v6H4c-0.806,0-1-0.555-1-1v-6H1.328c-0.598,0-0.47-0.324-0.06-0.748L9.292,2.22 C9.487,2.018,9.743,1.918,10,1.908c0.257,0.01,0.513,0.109,0.708,0.312l8.023,8.031C19.142,10.676,19.27,11,18.672,11z'/%3E%3C/svg%3E");
```

Now, all we need to do is replace the `background-image` property with `mask-image` and add `background: currentcolor;` to ensure the SVG icon color changes based on its parent's color. This provides an easy way to manage SVG icon colors.

```css
background: currentcolor;
mask-image: url("URL-encoded SVG");
```

To make the mask work, we also need to ensure the element is a block element by adding `display: block` or other properties that turn an element into a block element. Additionally, we must specify `width` and `height`.

Adding `mask-repeat: no-repeat;` and `mask-position: center;` (similar to background images) ensures the icon is centered and not repeated as a tile.

```css
.icon-home {
  display: block;
  width: 24px;
  height: 24px;
  background: currentcolor;
  mask-repeat: no-repeat;
  mask-position: center;
  mask-image: url("URL-encoded SVG");
}
```

The code above serves as a basic working example. If we replace `background: currentcolor;` with something like `background: linear-gradient(...);`, we can create a nice gradient icon, and animate it.

Creating SVG icons in this manner one by one can be tedious, so I developed a project called [SVG-in-CSS](https://peiwen.lu/project/svg-in-css) to help manage your SVGs and generate the necessary CSS code. It was inspired by both [URL-encoder for SVG](https://yoksel.github.io/url-encoder/) and [IcoMoon](https://icomoon.io/). You can also manage different projects by downloading the `svg-in-css.config.json` file and uploading it to restore your previous project settings. Check out the [source code repo](https://github.com/P233/svg-in-css) on GitHub, and maybe give it a star if you find it useful.

## Optimizing SVG with SVGO

I use [SVGO](https://github.com/svg/svgo) to optimize all SVG assets. As a result, the [SVG-in-CSS](https://peiwen.lu/project/svg-in-css) project includes a browser version of SVGO with the following configuration settings.

```javascript
{
  plugins: [
    "preset-default",
    "removeDimensions",
    {
      name: "prefixIds",
      params: {
        prefix: [FILE_NAME]
      }
    }
  ];
}
```

The `removeDimensions` plugin removes the `width` and `height` attributes while retaining the `viewBox` attribute, ensuring the SVG icon remains scalable. The `prefixIds` plugin prevents misuse of SVG filters by adding the file name as a filter prefix.

After optimization, the SVG icon code would look like this:

```css
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xml:space='preserve' viewBox='0 0 20 20'%3E%3Cpath d='M18.672 11H17v6c0 .445-.194 1-1 1h-4v-6H8v6H4c-.806 0-1-.555-1-1v-6H1.328c-.598 0-.47-.324-.06-.748L9.292 2.22c.195-.202.451-.302.708-.312.257.01.513.109.708.312l8.023 8.031c.411.425.539.749-.059.749z'/%3E%3C/svg%3E");
```

## Performance

If you're curious about the performance implications of using SVG icons in this manner, I've created a comparison page at https://svg-in-css-performance-test.vercel.app for your consideration. In my opinion, if you have fewer than 100 SVG icons on a single page, the performance impact is minimal and acceptable. However, if you have hundreds of icons, it's best to avoid this method.