20: Styling Inline SVG – Powers and Limitations

(Updated on )

Inline SVG can be “styled” in the sense that it already has fills and strokes and whatnot the second you put it on the page. That’s awesome and a totally fine way to use inline SVG. But you can also style inline SVG through CSS, which is kinda awesome because, I imagine for a lot of us, CSS is where we feel powerful and comfortable.

It works pretty much how you would expect. Here’s a simple example:

<svg>
  <rect class="my-rect" ... />
</svg>
.my-rect {
  fill: blue; /* remember it's fill not background, teamsters */
}

CSS has a bit “more power”, you could say, than style attributes on the SVG elements themselves. If that <rect> had like fill="red" on it, the CSS would still “win”. You might think the opposite because it seems like style attributes would be powerful like inline styles, but they aren’t. Inline styles are still powerful though.

Likewise, CSS rules don’t cascade down if there is anything at all more specific happening. For instance:

<g class="parent">
  <rect fill="blue" />
</g>
.parent {
  fill: red;
}

The CSS loses in this case, because the blue is being applied more specifically to the rect.

If I’m planning to style SVG through CSS, I generally find it easiest to just leave style attributes off the SVG elements entirely.

Important thing to know alert!

We’ve spent time talking about <use>. Say this is the situation:

<svg>
  <symbol viewport="0 0 100 100" id="thing">
    <rect class="child" ... />
  <symbol>
</svg>

<svg class="parent">
  <use xlink:href="#thing" />
</svg>

Ultimately that “child” gets put in that “parent” right? Right. So this should work?

.parent .child {
  fill: red;
}

But it does not.

That the way <use> works, it clones that <symbol> and puts it into a “Shadow DOM” in that second SVG. You can’t penetrate through that shadow DOM with a selector like that. Just doesn’t work. Perhaps someday there will be a solution, but there isn’t right now.

You can do like:

.parent {
  fill: red;
}

And that fill will cascade through and affect the child elements if there is nothing more specific in the way. Or

.child {
  fill: red;
}

and affect all instances of that child. But just not both.

If you do need differently styled versions of the same thing…

Just duplicate the <symbol> or whatever you need. The vast majority of the information will be identical, and GZip eat identical text for breakfast.