Valmistame SVG sümbolite ja CSS-muutujatega mitmevärvilisi ikoone

Valmistame SVG sümbolite ja CSS-muutujatega mitmevärvilisi ikoone

Ammu on möödas päevad, mil veebi ikoonide tegemiseks kasutati pilte ja CSS-spritte. Veebifondide plahvatusega on ikoonifondid muutunud teie veebiprojektides ikoonide kuvamise esimeseks lahenduseks.

Fondid on vektorid, nii et te ei pea resolutsiooni pärast muretsema. Neil on samad CSS-i omadused nagu tekstil. Selle tulemusena on teil täielik kontroll suuruse, värvi ja stiili üle. Saate lisada teisendusi, efekte ja kaunistusi, näiteks pöördeid, allajooni või varje.

Ikoonifondid pole siiski täiuslikud , mistõttu eelistab üha rohkem inimesi kasutada SVG-siseseid pilte. CSS Tricks kirjutas loetelu piirkondadest, kus ikoonifondid jäävad võrreldes SVG natiivsete elementidega lühikeseks: teravus, positsioneerimine või isegi rikked domeenidevahelise laadimise, brauserispetsiifiliste vigade ja reklaamiblokeerijate tõttu. Nüüd saate enamikust neist probleemidest mööda hiilida, tehes ikoonifondid üldiselt turvaliseks valikuks.

Siiski on ikoonifondide puhul täiesti võimatu üks asi: mitmevärviline tugi . Ainult SVG saab seda teha.

TL; DR : selles postituses käsitletakse põhjalikult, kuidas ja miks. Kui soovite kogu mõtteprotsessi mõista, lugege edasi. Vastasel juhul saate CodePenil vaadata lõplikku koodi.

SVG sümbolikoonide seadistamine

Sisseehitatud SVG-de probleem on see, kui paljusõnalised nad on. Te ei soovi kõiki neid koordinaate kopeerida / kleepida iga kord, kui peate kasutama sama ikooni. See oleks korduv, raskesti loetav ja valus säilitada.

SVG sümbolikoonidega on teil igast SVG elemendist üks eksemplar ja saate need viitega kõikjale installeerida.

Alustuseks lisate SVG sisse, peidate selle, mähite selle a-ni ol> and identify it wi th an id attribute.


The full SVG markup is included once and hidden in the HTML.

Then, all you have to do is instantiate the icon with a se> element.

This will display an exact copy of your original SVG icon.

That’s it! Pretty nice, right?

You probably noticed the funny xlink:href attribute: this is the link between your instance and the original SVG.

It’s important to mention that xlink:href is a deprecated SVG attribute. Even if most browsers still support it, you should use href instead. Now the thing is, some browsers like Safari don’t support SVG resource references through the href attribute, so you still need to provide xlink:href.

To be safe, provide both attributes.

Adding some color

Unlike with fonts, color doesn’t have any effect on SVG icons: you must use the fill attributes to define a color. This means that they won’t inherit parent text color like icon fonts do, but you can still style them in CSS.

// HTML 
// CSS.icon { width: 100px; height: 100px; fill: red;}

From here, you can create other instances of the same icon with a different fill color.

// HTML 
// CSS.icon { width: 100px; height: 100px;}.icon-red { fill: red;}.icon-blue { fill: blue;}

It works, but this isn’t exactly what we want. So far, all we have done can be achieved with a regular icon font. What we want is to have a different color for each part of the icon. We want to fill each path with a different color, without altering other instances, and we want to be able to override it if necessary.

At first, you might be tempted to rely on specificity.

// HTML  my-first-icon    
// CSS.icon-colors .path1 { fill: red;}.icon-colors .path2 { fill: green;}.icon-colors .path3 { fill: blue;}

This won’t work.

We’re trying to style .path1, .path2 and .path3 as if they were nested in .icon-colors, but technically speaking they’re not. The se> element is n’t a placeholder that gets replaced by your SVG definition. I t’s a reference which clones the content it’s pointing to int o the shadow DOM ?

What can we do then? How can we affect children content in a scoped way when said children aren’t in the DOM?

CSS variables to the rescue

In CSS, some properties are inherited from ancestors to children. If you assign a text color to the body, all the text in the page will inherit that color until it’s overridden. The ancestor isn’t aware of the children, but the inheritable styles are still propagated.

In our early example, we inherited the fill property. Look again, and you’ll see that the class in which we declared a fill color is appended on the instances, not the definitions. This is how we were able to get different colors for each instance of a single definition.

Now here’s the problem: we want to pass different colors to different paths of the original SVG, but there’s only one fill attribute we can inherit from.

Meet CSS variables.

CSS variables are declared within rulesets just like any other property. You can name them anything you want, and assign them any valid CSS value. Then, you declare it as a value for itself, or any child property, and it will be inherited.

.parent { --custom-property: red; color: var(--custom-property);}

All children of .parent will have red text.

.parent { --custom-property: red;}.child { color: var(--custom-property);}

All .child nested in .parent elements will have red text.

Now let’s apply this concept to our SVG symbol. We’ll use the fill attribute on each path of the SVG definition, and set them to different CSS variables. Then, we’ll assign them different colors.

// HTML my-first-icon 
// CSS.icon-colors { --color-1: #c13127; --color-2: #ef5b49; --color-3: #cacaea;}

And… it works! ?

From now on, all we need to do to create an instance with a different color scheme is to create a new class.

// HTML 
// CSS.icon-colors-alt { --color-1: brown; --color-2: yellow; --color-3: pink;}

If you still want to have monochrome icons, you don’t have to repeat the same color on every CSS variable. Instead, you can declare a single fill rule: because CSS variables aren’t defined, it will fall back on your fill declaration.

.icon-monochrome { fill: grey;}

Your fill declaration will work because the fill attributes on the original SVG are set with undefined CSS variables values.

What to name my CSS variables?

There are usually two routes you can take when it comes to naming things in CSS: descriptive or semantic. Descriptive means calling a color what it is: if you’re storing #ff0000, you’d call it --red. Semantic means calling the color by how it’s applied: if you’re using #ff0000 for the handle of a coffee cup, you’d call it --cup-handle-color.

Descriptive names might be your first instinct. It feels DRYer since #ff0000 can be used for other things than the handle of the coffee cup. A --red CSS variable is reusable for other icon paths that need to be red. After all, this is how utility-first CSS works and it’s a fine system.

Problem is, in our case we can’t apply granular classes to the elements we want to style. Utility-first principles can’t apply, because we have a single reference for each icon, and we have to style it through class variations.

Using semantic class names, like --cup-handle-color for example, makes more sense for this use case. When you want to change the color of a part of an icon, you instantly know what it is and what to override. The class name will remain relevant no matter what color you assign.

To default or not to default

It’s tempting to make the multi-colored version of your icons their default state. This way, you could use them with no need for extra styling, and you would add your own classes only when necessary.

There are two ways to achieve that: :root and var() default.


You can define all your CSS variables on the :root selector. This keeps them all in one place and allows you to “share” similar colors. :root has the lowest priority, so it remains easy to override.

:root { --color-1: red; --color-2: green; --color-3: blue; --color-4: var(--color-1);}
.icon-colors-alt { --color-1: brown; --color-2: yellow; --color-3: pink; --color-4: orange;}

However, there are major drawbacks to this method. First, keeping color definitions separate from their respective icons can be confusing. When you decide to override them, you have to go back and forth between the class and the :root selector. But more importantly, it doesn’t allow you to scope your CSS variables, thus keeps you from reusing the same names.

Most of the time, when an icon only uses one color, I use the --fill-color name. It’s simple, understandable, and it makes sense to use the same name for all icons that only need one fill color. If I have to declare all variables in the :root declaration, I can’t have several --fill-color. I’ll be forced to define --fill-color-1, --fill-color-2, or use namespaces like --star-fill-color, --cup-fill-color.

var() default

The var() function, which you use to assign a CSS variable to a property, can take a default value as a second argument.


Until you define --color-1, --color-2 and --color-3, the icon will use the default values you set for each th>. This solves the global scope issue we have when using :root, but be car eful: you now have a default value and it’s doing its job. As a result, you can’t use a s ingle fill declaration to define monochrome icons anymore. You’ll have to assign the color to every CSS variable used on the icon, one by one.

Setting default values can be useful, but it’s a tradeoff. I suggest you don’t make it a habit, and only do it when it makes sense for a given project.

How browser-friendly is all that?

CSS variables are compatible with most modern browsers, but as you probably expect it, Internet Explorer doesn’t support it at all. Not even IE11, and since development was discontinued in favor of Edge, there’s no chance it will ever get up to speed.

Now, just because a feature isn’t supported by a browser you need to cater to, that doesn’t mean you have to rule it out altogether. In such cases, go for graceful degradation: offer multi-colored icons to modern browsers, and provide a fallback fill color for older ones.

What you want to do is set a declaration that will only work if CSS variables aren’t supported. This can be achieved by setting the fill property to the fallback color: if CSS variables are supported, it won’t even be taken into account. If they’re not, your fill declaration will apply.

If you’re using Sass, this can be abstracted into a @mixin.

@mixin icon-colors($fallback: black) { fill: $fallback; @content;}

We can now define color schemes without worrying about browser compatibility.

.cup { @include icon-colors() { --cup-color: red; --smoke-color: grey; };}
.cup-alt { @include icon-colors(green) { --cup-color: green; --smoke-color: grey; };}

Passing the CSS variables in the mixin through @content is optional. If you do it outside, the compiled CSS will be the same. But it can be helpful to package it all in one place: you can fold snippets in your editor and visually identify declarations that go together.

Check out this pen on different browsers. On up-to-date versions of Firefox, Chrome, and Safari, the last two cups will respectively be red with grey smoke and blue with grey smoke. On Internet Explorer and Edge before version 15, the third cup will be all red and the fourth will be all blue! ✨

If you want to learn more about SVG symbol icons (and SVG in general), I strongly suggest you read everything by Sara Soueidan. And if you have any question about CSS symbol icons, don’t hesitate to hit me up on Twitter!

Originally published at