60

I am trying to use CSS variables in my SVG (which is set as a background image) for the fill color, but am having difficulty in getting it to work. It shows the default black, but when I inspect it I can see that the css variable is there and showing my desired color.

HTML

<div class="test">
  Testing the css variable color
</div>

<div class="icon">

</div>

CSS

:root {
  --primary-color: hsl(332, 61%, 78%);
}

div {
  width: 100px;
  height: 100px; 
}

.test {
  background: var(--primary-color);
}

.icon {
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 129 129'%3E%3Cpath d='m121.3,34.6c-1.6-1.6-4.2-1.6-5.8,0l-51,51.1-51.1-51.1c-1.6-1.6-4.2-1.6-5.8,0-1.6,1.6-1.6,4.2 0,5.8l53.9,53.9c0.8,0.8 1.8,1.2 2.9,1.2 1,0 2.1-0.4 2.9-1.2l53.9-53.9c1.7-1.6 1.7-4.2 0.1-5.8z' fill='var(--primary-color)' /%3E%3C/svg%3E");
}

Here is a codepen to check out!

I've seen CSS Variables being used in SVG here but I'm not sure if it's possible to do with background images? I'm new to both using SVG and CSS variables so I'm not sure if I'm doing something wrong... Would love some insight as to why it's not rendering the color properly!

sammiepls
  • 1,340
  • 2
  • 13
  • 19
  • Hey! Yes the background of the square is supposed to change color according to the CSS variable (primary-color). The SVG fill color is also supposed to change but in this case it's not. The square is just to show that the CSS variable color is being applied to it :) – sammiepls Jul 18 '18 at 08:04
  • 1
    no it's impossible, you cannot use CSS variable like this when it's called with URL – Temani Afif Jul 18 '18 at 08:13
  • 1
    duplicate of : https://stackoverflow.com/questions/42330075/is-there-a-way-to-interpolate-css-variables-with-url – Temani Afif Jul 18 '18 at 08:19

8 Answers8

33

You can include the SVG as a mask-image instead of a background-image. Remember to set the background-color to the CSS variable for the .icon block.

.icon {
  background: var(--primary-color);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 129 129'%3E%3Cpath d='m121.3,34.6c-1.6-1.6-4.2-1.6-5.8,0l-51,51.1-51.1-51.1c-1.6-1.6-4.2-1.6-5.8,0-1.6,1.6-1.6,4.2 0,5.8l53.9,53.9c0.8,0.8 1.8,1.2 2.9,1.2 1,0 2.1-0.4 2.9-1.2l53.9-53.9c1.7-1.6 1.7-4.2 0.1-5.8z' /%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 129 129'%3E%3Cpath d='m121.3,34.6c-1.6-1.6-4.2-1.6-5.8,0l-51,51.1-51.1-51.1c-1.6-1.6-4.2-1.6-5.8,0-1.6,1.6-1.6,4.2 0,5.8l53.9,53.9c0.8,0.8 1.8,1.2 2.9,1.2 1,0 2.1-0.4 2.9-1.2l53.9-53.9c1.7-1.6 1.7-4.2 0.1-5.8z' /%3E%3C/svg%3E");
} 
Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51
26

Okay here we go... I will first explain why it does not work and then I will show an alternative.

Why your approach doesn't work

In your example the svg is not part of the DOM. So you cannot use css to modify the attributes of the svg.

What you are doing is adding an inline-style to the svg in your url. Since the browser does not recognise --primary-color as a color it doesn't work.

An alternative approach

An alternative approach is to put the svg in the html and fake a background. I did this by absolute positioning the svg and moving it to the background with z-index.

Do note you will have to modify the svg or the positioning to place the background in the way you want. Normally you would use background-size for this. But with some effort you can replicate this behaviour within the svg or position it better by using css.

:root {
  --primary-color: hsl(332, 61%, 78%);
}

div {
  width: 100px;
  height: 100px; 
}

.test {
  background: var(--primary-color);
}
.icon{ /*postion relative for absolute positioning to work*/
  position: relative; 
}
.icon>svg{
  position: absolute;
  top: 0px;
  right: 0px;
  left: 0px;
  bottom: 0px;
  z-index: -1;
}
.icon>svg>path{ /*target the image with css*/
  fill: var(--primary-color);
}
<div class="test">
  Testing the css variable color
</div>

<div class="icon">
  <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 129 129' id='background'><path d='m121.3,34.6c-1.6-1.6-4.2-1.6-5.8,0l-51,51.1-51.1-51.1c-1.6-1.6-4.2-1.6-5.8,0-1.6,1.6-1.6,4.2 0,5.8l53.9,53.9c0.8,0.8 1.8,1.2 2.9,1.2 1,0 2.1-0.4 2.9-1.2l53.9-53.9c1.7-1.6 1.7-4.2 0.1-5.8z'/> </svg>
  <p>Text goes here...</p>
</div>
Rob Monhemius
  • 4,822
  • 2
  • 17
  • 49
  • Thanks for your answer! Very clear and helped me to understand why my CSS variables weren't working. Thanks for the example as well, unfortunately not sure if I could go this route as the reason I was trying to use the SVG as a background was to replace the select tag dropdown (aghh selects are so hard to work with!). But great explanation :) – sammiepls Jul 19 '18 at 03:47
10

Don't include svg as background, by doing that you don't have control over it's fill, instead try adding it inline in html and via css you can control the fill via css variable, please check the working example below, hope it helps :)

:root {
  --primary-color: hsl(332, 61%, 78%);
}

div {
  width: 100px;
  height: 100px;
}

.test {
  background: var(--primary-color);
}

.icon {
  color: var(--primary-color);
  fill: currentColor;
  width: 64px;
  height: 64px;
}
<div class="test">
  Testing the css variable color
</div>
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 129 129">
  <path d="m121.3,34.6c-1.6-1.6-4.2-1.6-5.8,0l-51,51.1-51.1-51.1c-1.6-1.6-4.2-1.6-5.8,0-1.6,1.6-1.6,4.2 0,5.8l53.9,53.9c0.8,0.8 1.8,1.2 2.9,1.2 1,0 2.1-0.4 2.9-1.2l53.9-53.9c1.7-1.6 1.7-4.2 0.1-5.8z"/>
</svg>
Girisha C
  • 1,922
  • 1
  • 12
  • 20
  • 1
    Thanks for taking the time to answer! The reason I went the route of using SVG as a background was to try and replace the dropdown arrow in the select tag (sorry did not state that in the question/codepen!)... Not sure if using an actual SVG inside the tag would work out for me. But good to know that CSS variables would work in inline SVGs! – sammiepls Jul 19 '18 at 03:45
  • 1
    in that case use font icons, upload you desired svg file to https://icomoon.io/app/#/select app or select one from there Icons Library and generate the font, then u will be able to use it similar to fontawsome, then u will have control over the color. :) – Girisha C Jul 19 '18 at 07:10
  • 1
    fill: currentColor; <- where is that `currentColor` coming from? – Bullsized Mar 31 '22 at 13:11
  • 1
    @Bullsized `currentColor` is a CSS keyword / value that is the current color of that element or its parent's color. – Jacob Jun 20 '22 at 19:54
3

Using the <symbol> tag and CSS variables

CSS variables inherit the fill property. Therefore, it is possible to assign (declare) a variable inside the <symbol> tag, the value of which can be subsequently changed many times for each instance of the svg element.

<symbol id="monstr"> 
<rect width="100%" height="100%" fill="transparent" />
<path id="face" fill="var(--color-face)" d="M15.4,34.1L24,37l8.6-2.9c1.9-0.6,3-2.6,2.6-4.6L33,20H15l-2.2,9.5C12.3,31.5,13.5,33.5,15.4,34.1z"/>
<path  id="nose" fill="var(--color-nose)" d="M29,30l-3-3h-4l-3,3v7c0,1.1,0.9,2,2,2h6c1.1,0,2-0.9,2-2V30z"/>  

Each path, circle, ellips, etc. can be assigned its own variable fill = "var (- color-face)" for fill and then change its value in the external style sheet:

.monstr-colors { --color-face: #7986CB; --color-nose: #9FA8DA; }

This technique creates powerful and flexible styling options for multicolor svg.

For example, for one state of an icon, we can assign one color scheme, and with : hover, assign a different color set to the same icon.

.monstr-colors {
  --color-face: #7986CB;
  --color-nose: #9FA8DA;
} 
.monstr-colors:hover {
  --color-face: #3F8B4D;  
  --color-nose: #58C46C;
)    

Below is an example, according to your taste, the color scheme of the image is easily created and changed:

.monstr-colors {
  --color-face: #7986CB;
  --color-nose: #9FA8DA;
  --color-hair-right:#3949AB;
  --color-hair-right2:#3949AB;
  --color-hair-left:#3949AB;
  --color-hair-left2:#3949AB;
  --color-eye-right:#1A237E;
  --color-pupil-right:#77006B;
  --color-eye-left:#1A237E;
  --color-pupil-left:#77006B;
  --color-ellipse1:#9FA8DA;
  --color-ellipse2:#7986CB;
  --color-ellipse3:#C5CAE9;
}  

.monstr-colors:hover {
  --color-face: #3F8B4D;  
  --color-nose: #58C46C;
  --color-hair-right:gold;
  --color-hair-right2:#FFBB00;
  --color-hair-left:gold;
  --color-hair-left2:#FFBB00;
  --color-eye-right:#77006B;
  --color-pupil-right:#FF4151;
  --color-eye-left:#77006B;
  --color-pupil-left:#FF4151;
  --color-ellipse1:#FFDD00;
  --color-ellipse2:#C1A700;
  --color-ellipse3:#FFEE7D;
} 
<svg  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"  width="192" height="192" viewBox="0 0 48 48" > 
<symbol id="monstr"> 
<rect width="100%" height="100%" fill="transparent" />
<path id="face" fill="var(--color-face)" d="M15.4,34.1L24,37l8.6-2.9c1.9-0.6,3-2.6,2.6-4.6L33,20H15l-2.2,9.5C12.3,31.5,13.5,33.5,15.4,34.1z"/>
<path  id="nose" fill="var(--color-nose)" d="M29,30l-3-3h-4l-3,3v7c0,1.1,0.9,2,2,2h6c1.1,0,2-0.9,2-2V30z"/>


    <path id="hair-right" fill="var(--color-hair-right)" d="M31,7c-0.5,0-1,0.4-1,1c0,0,0,0,0,0c-0.4,0-0.8,0.2-0.9,0.6c-0.2,0.5,0,1.1,0.6,1.3   C30,10.1,40,14.4,40,32.4V37c0,0.6,0.4,1,1,1s1-0.4,1-1v-4.6c0-14.4-6.1-20.7-9.5-23.1C35.9,10.3,44,14.7,44,34c0,0.6,0.4,1,1,1   s1-0.4,1-1C46,7.7,31.6,7,31,7z"/>
    <path id="hair-right2" fill="var(--color-hair-right2)" d="M29.5,10.1c-0.5-0.3-1.1-0.1-1.3,0.4c-0.3,0.5-0.1,1.1,0.4,1.4c0.1,0,7.5,4.3,7.5,20.1v8c0,0.6,0.4,1,1,1   s1-0.4,1-1v-8C38,14.9,29.8,10.3,29.5,10.1z"/>
    <path id="hair-left" fill="var(--color-hair-left)" d="M18.4,9.9c0.5-0.2,0.8-0.8,0.6-1.3C18.8,8.2,18.4,8,18,8c0,0,0,0,0,0c0-0.6-0.5-1-1-1C16.4,7,2,7.7,2,34   c0,0.6,0.4,1,1,1s1-0.4,1-1c0-19.6,8.1-23.8,11.6-24.7C12.2,11.6,6,17.9,6,32.4V37c0,0.6,0.4,1,1,1s1-0.4,1-1v-4.6   C8,14.4,18,10.1,18.4,9.9z"/>
    <path id="hair-left" fill="var(--color-hair-left2)" d="M18.5,10.1C18.2,10.3,10,14.9,10,32v8c0,0.6,0.4,1,1,1s1-0.4,1-1v-8c0-15.8,7.4-20.1,7.5-20.1   c0.5-0.3,0.7-0.9,0.4-1.4C19.6,10,19,9.9,18.5,10.1z"/>

<path id="eye-right" fill="var(--color-eye-right)" d="M25,24.9c0,0,0.2,1.3,0.6,1.7s3.3,2.5,5.9-0.9c1.2-1.5,0.6-3.8,0.6-3.8S29.4,24.1,25,24.9z"/>
<circle id="pupil-right" cx="28" cy="25" r="1.5" fill="var(--color-pupil-right)" />
<path id="eye-left" fill="var(--color-eye-left)" d="M15.8,21.8c0,0-0.6,2.3,0.6,3.8c2.6,3.4,5.5,1.4,5.9,0.9c0.4-0.4,0.6-1.7,0.6-1.7  C18.6,24.1,15.8,21.8,15.8,21.8z"/> 
<circle id="pupil-left" fill="var(--color-pupil-left)" cx="20" cy="25" r="1.5" fill="red" />
<ellipse  id="ellipse1" fill="var(--color-ellipse1)" cx="24" cy="15" rx="12" ry="10"/>
<ellipse id="ellipse2" fill="var(--color-ellipse2)" cx="24" cy="13.8" rx="10" ry="7.8"/>
<ellipse id="ellipse3" fill="var(--color-ellipse3)" cx="24.2" cy="12.2" rx="8" ry="6.2"/> 

</symbol> 
 <svg class="monstr-colors">
  <use  xlink:href="#monstr" />
 </svg>
</svg>

Below is a combined example with styling three instances of the same image.

Each instance of the created <use xlink: href = "# monstr"/> has its own color scheme, which is linked to the nested svg class.

<div class="container">
<svg class="color-monstr">
<use xlink:href="#monstr" transform="scale(3)" />
</svg> 
 </div>
<div class="container2">
<svg class="color-monstr2">
<use xlink:href="#monstr" transform="scale(2)"  />
</svg> 
</div> 

<div class="container3">
<svg class="color-monstr3">
<use xlink:href="#monstr" transform="scale(3)"  />
</svg> 
</div>          

With :hover, each instance replaces the color scheme with the color scheme of the adjacent instance.

.parent {
position:relative;
}
.container {
width:400px;
height:400px;
position:absolute;
top:0;
} 
.container2 {
position: absolute;
top:0;
left:150px;

} 

.container3 {
position: absolute;
top:0;
left:240px;

} 

.color-monstr {
  --color-ears: #459E48;
  --color-horn-right: #388E3C;
  --color-horn-left: #388E3C;
  --color-face:#4CAF50;
  --circle-horn-left:#8BC34A;
  --circle-horn-right:#8BC34A;
  --eye-right:#FFF9C4;
  --eye-left:#FFF9C4;
  --pupil-right:#263238;
  --pupil-left:#263238;
  --mouth:#173027;
  }  
  
  .color-monstr:hover {
  --color-ears: #504F7A;
  --color-horn-right: #504FF6;
  --color-horn-left: #504FF6;
  --color-face:#807FC4;
  --circle-horn-left:#FF00AE;
  --circle-horn-right:#FF00AE;
  --eye-right:#FFDBF4;
  --eye-left:#FFDBF4;
  --pupil-right:#263238;
  --pupil-left:#263238;
  --mouth:#FFDBF4;
  }  
  
  .color-monstr2 {
  --color-ears: #504F7A;
  --color-horn-right: #504FF6;
  --color-horn-left: #504FF6;
  --color-face:#807FC4;
  --circle-horn-left:#FF00AE;
  --circle-horn-right:#FF00AE;
  --eye-right:#FFDBF4;
  --eye-left:#FFDBF4;
  --pupil-right:#263238;
  --pupil-left:#263238;
  --mouth:#FFDBF4;
  }   
  
  .color-monstr2:hover {
  --color-ears: #770051;
  --color-horn-right: #388E3C;
  --color-horn-left: #388E3C;
  --color-face:#FFDD00;
  --circle-horn-left:#D0FF00;
  --circle-horn-right:#A0C400;
  --eye-right:#FFF9C4;
  --eye-left:#FFF9C4;
  --pupil-right:#263238;
  --pupil-left:#263238;
  --mouth:#FFF9C4;
  
  }  
  
  .color-monstr3 {
  --color-ears: #770051;
  --color-horn-right: #388E3C;
  --color-horn-left: #388E3C;
  --color-face:#FFDD00;
  --circle-horn-left:#D0FF00;
  --circle-horn-right:#A0C400;
  --eye-right:#FFF9C4;
  --eye-left:#FFF9C4;
  --pupil-right:#263238;
  --pupil-left:#263238;
  --mouth:#FFF9C4;
  } 
  
  .color-monstr3:hover {
  --color-ears: #459E48;
  --color-horn-right: #388E3C;
  --color-horn-left: #388E3C;
  --color-face:#4CAF50;
  --circle-horn-left:#8BC34A;
  --circle-horn-right:#8BC34A;
  --eye-right:#FFF9C4;
  --eye-left:#FFF9C4;
  --pupil-right:#263238;
  --pupil-left:#263238;
  --mouth:#173027;
  }
<div class="parent">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"   viewBox="0 0 48 48" > 
<symbol id="monstr">

<path id="ears"
 fill="var(--color-ears)"
  d="M12,31c-2.3,0-3.7-3.2-4-5c0.7-1.3,3.3-2,4-2c5.7-2.4,17.8-2.9,24,0c0.7,0,3.3,0.7,4,2c-0.3,1.8-1.7,5-4,5  C33.6,32.8,14.7,34.2,12,31z"/>
<g>
    <path id="horn-right"
   fill="var(--color-horn-right)" d="M40,8c-0.6,0-1,0.4-1,1c0,2.7-3.3,5-6,5c-0.6,0-1,0.4-1,1s0.4,1,1,1c3.7,0,8-3.1,8-7C41,8.4,40.6,8,40,8z"/>
    <path id="horn-left"
   fill="var(--color-horn-left)" d="M8,8c0.6,0,1,0.4,1,1c0,2.7,3.3,5,6,5c0.6,0,1,0.4,1,1s-0.4,1-1,1c-3.7,0-8-3.1-8-7C7,8.4,7.4,8,8,8z"/>
</g>
<path id="face"
 fill="var(--color-face)" d="M12,31v-7c0-9.2,5.3-16,12-16s12,6.8,12,16v7c-1.2,5.6-7,12-12,12S13.2,36.6,12,31z"/>
<g>
    <circle id="circle-horn-left" fill="var(--circle-horn-left)" cx="8" cy="9" r="3"/>
    <circle id="circle-horn-right" fill="var(--circle-horn-right)" cx="40" cy="9" r="3"/>
</g>
<g>
    <ellipse id="eye-right" fill="var(--eye-right)" cx="29" cy="26" rx="2" ry="4"/>
    <ellipse id="eye-left" fill="var(--eye-left)" cx="19" cy="26" rx="2" ry="4"/>
</g>
<g>
    <circle id="pupil-right" fill="var(--pupil-right)" cx="29" cy="27" r="1"/>
    <circle id="pupil-left" fill="var(--pupil-left)" cx="19" cy="27" r="1"/>
</g>
<path id="mouth" fill="var(--mouth)" d="M24,33c-4,0-5.8,3-5.8,3s2.6,0,5.8,0s5.8,0,5.8,0S28,33,24,33z"/>
</symbol> 
</svg>


<div class="container">
<svg class="color-monstr">
<use xlink:href="#monstr" transform="scale(3)" />
</svg> 
 </div>
<div class="container2">
<svg class="color-monstr2">
<use xlink:href="#monstr" transform="scale(2)"  />
</svg> 
</div> 

<div class="container3">
<svg class="color-monstr3">
<use xlink:href="#monstr" transform="scale(3)"  />
</svg> 
</div>

</div>
Alexandr_TT
  • 13,635
  • 3
  • 27
  • 54
2

Here is a better way or how I color my SVGs. As Inline SVGs have a bitter taste on readability of the rest of the code and adding them as background or mask, they can not be colored by a dark theme switch. Include them externally and load them inside an html object:

<object id="background-anim" class="svg" aria-hidden="true" data="./media/Background-fluid-lines-anim.svg" type="image/svg+xml"></object>

This SVG is now added externally. To set the colors you need to set for the desired colors a class inside of the SVG.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1080">
  <defs>
    <style>
        .svg-element__clrText{
            fill:rgb(255,255,255);
        }
        .svg-element__clrPrimary{
            fill:rgb(155,155,155);
        }
        .svg-element__clrBackground{
            fill:rgb(55,55,55);
        }
    </style>
  </defs>
  <path class="svg-element__clrText"></path>
</svg>

In your paths, you will set those classes to the desired elements that should adapt colors. Now you will manipulate those fill values with JavaScript and add the correct CSS Custom Porperties:

/*Optional: Listening to the clicked switch if you have one*/
document.querySelectorAll('.theme-toggle').forEach((toggle) => {
   toggle.addEventListener('click', () => themeToggle);
});
window.themeToggle = function themeToggle() {
   document.documentElement.classList.toggle('darkMode');
   //this .darkmode{} class changes the values of my variables set to white mode in the ':root{}' in the css file
   loadTheme()
}
function loadTheme(){
   /* For loading more than just the colors */
   loadThemeIcon();
   loadSVGColors();
}

/* Now here comes the important part */
function loadSVGColors() {
   const svgs = document.querySelectorAll('.svg'); //getting NodeList of SVGs
   const style = getComputedStyle(document.body), //getting CSS variables inside of js
   svgs.forEach(svg => {
               element = svg.contentDocument; //get the actual svg that is loaded into the object element
         const clrText = svg.querySelectorAll('.svg-element__textClr'), //searching inside of the SVG after my classes
         textClr.forEach(element => { //NodeList to Array
              element.style.fill = style.getPropertyValue('--clr-text');//whatever variables you have set or you can toggle a class that has only this css variable inside
         });

         /* [...and so on]*/
         const primeClr = element.querySelectorAll('.svg-element__primeClr');
         primeClr.forEach(e => {
              e.style.fill = style.getPropertyValue('--clr-primary');
         });
   });
}

I guess there is a better performance when you add the SVGs inside the HTML with fill: var(--clr-text), however after having 50kb of code per SVG leads to lots of scrolling

I know this goes further than the question but I would have been glad to find this before under these SO posts.

Benedict Lang
  • 103
  • 1
  • 4
1

I stumbled upon this for a very similar case and want to share a completely different approach.

Use the css multi background feature (MDN Docs)

First you need to make the part of your svg that you want to color transparent with fill="transparent"
Now you can just specify multiple backgrounds, where one can be your variable, for example

body {
    background: url('path/to/your/svg/or/data-url'), var(--background);
}
Nicolas Gehlert
  • 2,626
  • 18
  • 39
-2
<path class="class-name"/>

:root {
   --color : #123456;
}
.class-name {
   fill : var(--color)
}

try this

  • 2
    While this code may provide a solution to the question, it's better to add context as to why/how it works. This can help future users learn and eventually apply that knowledge to their own code. You are also likely to have positive feedback/upvotes from users, when the code is explained. – Amit Verma Feb 24 '21 at 07:46
-3

If you need to control only one color, you can use currentColor keyword in the fill property and change it through css color property.

.icon {
  color: var(--primary-color);
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 129 129'%3E%3Cpath d='m121.3,34.6c-1.6-1.6-4.2-1.6-5.8,0l-51,51.1-51.1-51.1c-1.6-1.6-4.2-1.6-5.8,0-1.6,1.6-1.6,4.2 0,5.8l53.9,53.9c0.8,0.8 1.8,1.2 2.9,1.2 1,0 2.1-0.4 2.9-1.2l53.9-53.9c1.7-1.6 1.7-4.2 0.1-5.8z' fill='currentColor' /%3E%3C/svg%3E");
}
Egor Kolesnikov
  • 163
  • 1
  • 4