I have an SVG with a number of rects that change fill colour as they move down the SVG (in a sort of gradient look). Users are able to choose a solid fill colour to 'theme' their experience. This colour replaces the 'gradient' color of the rects. I'm doing this using CSS variables.

However, I want to default back to the fill colour defined in the SVG if they don't choose a theme colour. In this case the CSS variable is set to '' making it invalid. For other elements I'm using a default that the element falls back to. I can't do this with the SVG rects as they're all different. I tried removing the default but I believe this sets the fill to it's initial CSS value, which is transparent.

If I have the following rect: <rect id="rect" fill="#000000" x="0" y="0" width="200" height="50" rx="6"></rect> and the following CSS: rect { fill: var(--preview-primary); } I'd expect it to be black when --preview-primary is invalid, but it's transparent.

Is there a way I can do this? Thanks.

2 Answers

2
Kaiido On Best Solutions

No, there is no way to fallback to the fill attribute.

Having a valid fill CSS rule will take precedence over the fill attribute. For CSSOM, var(--anything) is always a valid rule. If ever the computation of the var() function is invalid, then it will fallback to the default value. And the default value for fill is black:

#f {
  fill: var(--foobar);
}
<svg>
  <rect id="f" x="0" y="0" height="80" width="80" fill="green"/>
</svg>

So to workaround this situation, you have a few choices available:

  • If you can't modify your SVG, you can toggle-on user-selected rules only when the value is not "".

sel.onchange = e => {
  document.documentElement.style
    .setProperty('--selected-color', sel.value);
  // toggle a class so we know we have to handle it
  document.documentElement.classList
    .toggle('user-selected-color', sel.value !== "");
};
.user-selected-color rect {
  fill: var(--selected-color);
}
select{vertical-align:top}
<svg height="180" width="180">
  <rect x="0" y="0" height="80" width="80" fill="green"/>
  <rect x="90" y="0" height="80" width="80" fill="blue"/>
  <rect x="0" y="90" height="80" width="80" fill="red"/>
  <rect x="90" y="90" height="80" width="80" fill="black"/>
</svg>

<select id="sel">
  <option value="">none</option>
  <option>orange</option>
  <option>pink</option>
  <option>violet</option>
  <option>aqua</option>
</select>

  • If you can, you could set each element's CSS color and then fallback to the CSS value currentColor:

sel.onchange = e => {
  document.documentElement.style
    .setProperty('--selected-color', sel.value);
};
rect {
  fill: var(--selected-color, currentColor);
}
select{vertical-align:top}
<svg height="180" width="180">
  <rect x="0" y="0" height="80" width="80" fill="green" style="color:green"/>
  <rect x="90" y="0" height="80" width="80" fill="blue" style="color:blue"/>
  <rect x="0" y="90" height="80" width="80" fill="red" style="color:red"/>
  <rect x="90" y="90" height="80" width="80" fill="black" style="color:black"/>
</svg>

<select id="sel">
  <option value="">none</option>
  <option>orange</option>
  <option>pink</option>
  <option>violet</option>
  <option>aqua</option>
</select>

0
Paulie_D On

Remove the fill style from the SVG and place a default "fallback" color in the CSS for when the variable has not been defined.

rect {
  fill: var(--preview-primary, 000);
}

rect {
  fill: var(--preview-primary, black);
}

.red {
  --preview-primary: red;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <rect x="10" y="10" height="100" width="100"/>
</svg>

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <rect class="red" x="10" y="10" height="100" width="100"/>
</svg>