Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SVG Fill not being applied in FireFox

Tags:

css

firefox

svg

I can't seem to figure out why Firefox is using the default svg fill color instead of the class's fill.

Here are the 3 fills when viewing the FF inspector:

CSS

SVG is being inserted via

<svg class="icon">
    <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#menu-bag"></use>
</svg>

It should be showing the .skip-link .icon fill of white (#fff) but it's actually using the SVG fill of #002649; If i change .skip-link .icon to .skip-link svg then it works fine. Why can I not use a class and instead but explicitly state the element??

Am I missing something obvious about how Firefox fills an SVG? This CSS works fine in other browsers.

like image 710
Gil Avatar asked Jan 09 '15 18:01

Gil


People also ask

What is SVG fill?

The fill of an SVG shape defines the color of the shape inside its outline. The surface of the SVG shape, in other words. The fill is one of the basic SVG CSS properties you can set for any SVG shape.


1 Answers

If the behavior was unique to Firefox prior to version 56, it was because #menu-bag refers to a <symbol> element.

The specs say that a re-used <symbol> should be implemented as if it were replaced by a nested <svg>. Firefox used to treat this literally in their shadow DOM. The shadow DOM isn't visible in your DOM inspector, but it is subject to CSS selectors.

Which means that this code:

<a href="#" class="skip-link">
    <svg class="icon">
        <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#menu-bag"></use>
    </svg>
</a>

WAs implemented like this:

<a href="#" class="skip-link">
    <svg class="icon">  
        <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#menu-bag">
          <!--Start of shadow DOM boundary-->
          <svg><!-- replacement for <symbol> -->
             <!-- graphics content -->
          </svg>
          <!--End of shadow DOM boundary-->
        </use>
    </svg>
</a>

The svg.icon matches your .skip-link .icon rule (and as Kyle Mitt points out, that rule will always take precedence over your a:hover svg rule). This value is also inherited by the <use> element.

However, the shadow-DOM <svg> doesn't get the inherited value, because it is styled directly with the svg rule. When you change your selector to .skip-link svg, or when you trigger the a:hover svg rule, then the hidden inner element gets the style directly applied because that SVG is also a descendent of the link.

As Robert Longson noted in the comments, this is not how it is supposed to work. It's a side effect of the way that Firefox implemented <use> elements as complete cloned DOM trees, which just happened to be hidden from your DOM inspector.

Here's a "working" example of your original problem. Which is to say, on Chrome, Safari, Opera, Firefox 56+ or IE you will see a green circle that isn't altered when you hover it, but on Firefox prior to version 56 you will see a blue circle that turns red on hover/focus.

svg {
    fill: navy;
}
a:hover svg, a:focus svg {
    fill: red;
}
.skip-link .icon {
    fill: green;
}
.icon {
    height: 50;
    width: 50;
}
 <a href="#" class="skip-link">
        <svg class="icon">
            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#menu-bag" />
        </svg>
</a>
<svg height="0" width="0">
    <symbol id="menu-bag" viewBox="-10 -10 20 20">
        <circle r="10" />
    </symbol>
</svg>

So what to do you if you need to support old versions of Firefox? You have two options, one of which you've already figured out by trial and error:

  1. Avoid setting default styles using the svg tag selector, and rely on normal style inheritance from the <use> element.

  2. Use selectors that intentionally select the shadow-<svg> to cancel out the defaults, while also making sure that they have the intended effect on other browsers.

One option would be to use a rule like the following, which would maintain the specificity of your original rule for other browsers:

.skip-link .icon, .skip-link .icon use>svg {
    fill: green;
}

The use>svg selector will never match anything except with the Firefox bug, so it is safe to use without side effects. (Originally, I'd just suggested adding svg to the end of the selector, but that could be problematic in certain situations.)

like image 133
AmeliaBR Avatar answered Sep 20 '22 15:09

AmeliaBR