Embedding SVG Icons in CSS

18 Sept 2023 [SVGCSS Mask]

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, 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 as an example. Here’s the SVG source code:

<?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

We can convert this icon to the URL-encoded version using the URL-encoder for SVG.

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.

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.

.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 like this:

Creating SVG icons in this manner one by one can be tedious, so I developed a project called SVG-in-CSS to help manage your SVGs and generate the necessary CSS code. It was inspired by both URL-encoder for SVG and IcoMoon. 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 on GitHub, and maybe give it a star if you find it useful.

Optimizing SVG with SVGO

I use SVGO to optimize all SVG assets. As a result, the SVG-in-CSS project includes a browser version of SVGO with the following configuration settings.

  plugins: [
      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:

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");


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.

If you're interested in continuing the discussion and receiving future updates, don't hesitate to follow me on Twitter, GitHub or DEV Community.