How to change SVG color with CSS
Table of contents
I saw a lot of cases in my career when CSS beginners exported same SVG icon files twice just to put it in the layout with different color.
Let's look on example:
Here we have a typical carousel component with arrows navigation. In rest state the arrow is grey, on hover - it changes its color to another. CSS beginners may want to just export 2 different SVGs: white one and blue one - and put it in CSS as 2 different SVG files. But let's think about this idea more deeply.
Why duplicating SVGs is bad idea
This approach opens several issues because it is not flexible:
- Browser will need to download 2 almost the same SVG files with the only difference in 1 internal attribute.
- You will consume more files space on the assets server with almost identical files.
- What if designer will decide to paint arrow into third color by click event? Introducing more colors for this layout element will require to create and upload even more similar SVG files.
What solution is better
Fortunately there is a way to colorize SVGs while having only one initial file.
Let me introduce you mask-image CSS rule. By utilizing this rule we can change our approach of showing SVG icon by moving from background: url(icon.svg)
rule to mask-image: url(icon.svg)
plus some additional mask
rules.
This mask-image
rule works very similar to Photoshop masks - it takes your HTML element and "cuts out" a figure inside it in the form of your SVG.
After that we can set any background color to our element and this "cut out" figure will be colorized to it.
Here is mask-image
rule usage example:
.arrow {
--arrow-svg: url('https://raw.githubusercontent.com/rodion-arr/blog/main/content/images/2023/08/colorize-svg/arrow.svg');
background: none;
outline: none;
border: none;
width: 40px;
height: 40px;
cursor: pointer;
/* initial arrow SVG color */
background: black;
/* mask-image rules */
mask-image: var(--arrow-svg);
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
-webkit-mask-image: var(--arrow-svg);
-webkit-mask-repeat: no-repeat;
-webkit-mask-size: contain;
-webkit-mask-position: center;
}
/* changing background of element will change SVG color*/
.arrow:hover {
background: red;
}
We can change background color for .arrow
to any without the need of adding new SVG files.
As you can see - there are 2 sets of mask rules: simple and webkit-prefixed. More info on this you can find here - Vendor Prefix
Working CSS example
SCSS mixin for SVG mask
As colorizing SVGs is so common task - those who use SCSS in projects may want to utilize an mixin for reusing set of mask rules in one place and pass a SVG url as parameter.
This mixin assumes that you have configured autoprefixer in your project.
Mask mixin:
// _svg.scss fils
@mixin mask($imgUrl) {
mask-image: $imgUrl;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
}
Usage:
@use "./svg";
.arrow {
// other rules here
@include svg.mask(url('https://raw.githubusercontent.com/rodion-arr/blog/main/content/images/2023/08/colorize-svg/arrow.svg'));
}