When you re-use SVG graphics with the <use> element, it inherits CSS styles from the new context. If the graphic you're re-using applies inherited styles, they can be customized in each icon instance.

This allows you to define basic SVG icons that can be customized—to a certain extent—at the time they are used.

However, with complex icons, you are limited by the fact that every part of the icon graphic inherits the same styles. For colors, you can get a little bit of extra flexibility with the currentColor keyword; this grabs the inherited value of the CSS color property, which isn't directly used in SVG. That is how I set the color of the paint blobs in the above example; the original graphic uses style="fill:currentColor;", and then the color property is set for each icon. The inherited fill property is used for the color of the palette shape itself.

(Note that the default value for the border-color property is currentColor, which is why the second icon's border changes to match the paint.)

But that is still limited. What if you wanted the paint palette icon to have, well, a complete customizable color palette? You would need to be able to define multiple different inheritable CSS color properties that you could set on the icon <svg> element and then reference within the graphic.

That's exactly what CSS Variables do!

I've been thinking about this idea for a while, but CSS Variables have only been a hypothetical, experimental feature. This morning, Chris Coyier's CSS Tricks blog alerted me to the fact that CSS Variables are now supported in Firefox 31. I could finally test it out!

UPDATE: CSS Variables are supported in Chrome as of version 48 (available in Canary November 2015, should shift to stable in early 2016).

UPDATED UPDATE: CSS Variables are now supported in MS Edge as of version 15 (released to the stable update chanel in April 2017).

UPDATED UPDATED UPDATE: The MS EdgeHTML 15 version of variables doesn't work with <use> elements! It's tragic. The fallback approaches in this post still work, but beware @supports tests lying to you.

If you're looking at this in Firefox (or if you're a visitor from the future, where other browsers now support CSS variables), the above icons each have a unique, multi-color paint palette. This is done by setting values for custom CSS properties on each icon, and accessing these values in the original SVG code with style settings like fill:var(--color_palette);.

Of course, if you're looking at that example in a browser that doesn't support CSS variables, you're seeing the default black fill. Ugh. For backwards compatibility, we need reasonable fallback values. There are a couple options:

  • Use SVG presentation attributes to set default colors for each element.
  • Use the inherited fill/stroke/color properties to create a basic color palette.

As a reminder, presentation attributes over-ride inherited CSS style properties, but are over-ridden by any valid CSS property declaration (inline or from a stylesheet) that directly affects that element.

I use a mix of both options in the following version of the example. The palette color is defined as a presentation attribute, and will always be the same unless you set the CSS --color_palette variable. That allows me to use both the fill and the color property to control paint colors, so I can at least have two customizable colors on each palette.

In Firefox 31+, the above looks exactly the same as the previous example. Well, except for the green border on the second icon. Be sure to declare border colors explicitly if you're going to be messing around with the color property.

There is one extra complication with CSS variables implementation that you should be aware of: When a variable hasn't been defined for a given element, (such as for the original icon), a var() declaration referencing that variable is treated as an unset statement. (Note correction from earlier version of this post.) That means that the fill styles revert to the inherited color, instead of using the color defined in the SVG presentation attribute or in the previous style declaration. To get around this, declare a fallback value within the var() function itself, like fill:var(--color_palette, #b84);:

One final note: The CSS spec allows you great flexibility when naming your variables. There are only two rules:

  • It must start with --.
  • It must be a valid CSS identifier token (i.e., contains only letters, digits, _ and -).

If you're going to be using CSS variables a lot in your code, you'll want to come up with your own naming scheme that makes it easy to remember which variable does what. In these examples, I've used the format --propertyName_graphic. That might be too specific for general use; in general, you'll want to declare variables that will be used in multiple properties. Something like --dataType_description might work better, e.g. --length_thickline could be used to set both SVG stroke widths and border thickness.