Making colorful icons using SVG characters and CSS variables

Original author: Sarah Dayan
  • Transfer
Icons

The days are long gone when images and CSS sprites were used for web icons. With the development of web fonts, icon fonts became number 1 for displaying icons on websites.

Fonts are vector, so you don’t have to worry about screen resolution. You can use the same CSS properties for them as for text. As a result, you have full control over their size, color and style. You can add effects to them, transform or decorate them. For example, rotate ( rotate), underline ( underline), or add a shadow ( text-shadow).

Icon fonts are not perfect , so an increasing number of people prefer to use embedded SVG images. There is an article on CSS Tricks that describes moments in which icon fonts are inferior to SVG elements.: sharpness, positioning, cross-domain loading failures, browser features and ad blockers. Now you can get around most of these problems, which generally makes using icon fonts safe.

Yes, another thing that is absolutely impossible when using icon fonts: multicolor support . Only SVG can do this.

TL; DR : this post allows you to understand how and why. If you want to understand the whole process, read on. Otherwise, you can see the final code on CodePen .


Customizing SVG Icon Symbols


The problem with embedded SVGs is that they are complex. You do not want to copy all these coordinates every time you need to use the same icon. This will produce repetitive, difficult to read, and hard-to-maintain code.

Using SVG characters allows you to have only one instance of each SVG element and use it anywhere with a link.

Start by adding the built-in SVG, hide it, wrap the contents in a tag, symboland set it id.

my-first-icon

The full markup of an SVG element is written once and is hidden.

Then all you have to do is create a copy of the icon using the element use.


You will get an exact copy of your original SVG icon.

Black cup

Here she is! Pretty sweet, right?

You probably noticed the attribute xlink:href- this is the link between your icon and the original SVG image.

It is important to note that this xlink:hrefis an obsolete SVG attribute. Even if most browsers still support it, you should usehref it instead . But the fact is that some browsers, for example, Safari, do not support links to SVG resources through an attribute href, so you still need to specify xlink:href.

For security, use both attributes.

Add color


Unlike fonts, the property colordoes not affect SVG icons: you must use the attribute fillto indicate the color. This means that they do not inherit the parent color of the text, but you can still style them through CSS.


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

And therefore, you can create other instances of the same icon in different colors.


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

It works, but it’s not exactly what we want. Everything that we have done so far can also be done using a regular icon font. What we want is to make each part of the icon a different color . We want to fill in with different colors each part of one icon without changing its other instances, and we want it to be possible to redefine these colors if necessary.

You may first have the idea of ​​relying on specificity.

my-first-icon

.icon-colors .path1 {
  fill: red;
}
.icon-colors .path2 {
  fill: green;
}
.icon-colors .path3 {
  fill: blue;
}


This will not work.

We are trying to set styles for .path1, .path2and .path3so, if they were nested in .icon-colors, but technically it is not. usean element is not a placeholder that is replaced with a specific SVG. This is a link that copies the contents of what it points to in the shadow DOM .

And then what shall we do ? How can we influence the contents of children when they say that children are not in the DOM?

CSS variables will help


In CSS, some properties are inherited by children from their ancestors. If you specify a text color for body, then all text on the page will inherit that color until it is overridden. The ancestor does not know children, but inherited properties are still transmitted.

In the example above, we inherit the property fill. Look again and you will see that the class in which we defined this color is filladded to the icon instances, and not to its definition. So we were able to get multi-colored copies of one source.

But here's the problem: we want to convey different colors for different parts of the original SVG icon, but there is only one attribute fillthat we can inherit.

Meet CSS variables .

CSS variables are declared in rule sets just like any other property. You can name them whatever you want and assign them any valid CSS value. Then you define through this variable the property value of the element itself or its child, and it will be inherited .

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

All children .parentwill have red text.

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

All .childnested in .parentwill have red text.

Now let's apply this concept to our SVG symbol. We will use the attribute fillfor each part pathin the definition of our SVG icon and set different CSS variables for them. Then we will assign them different colors.

my-first-icon

.icon-colors {
  --color-1: #c13127;
  --color-2: #ef5b49;
  --color-3: #cacaea;
}

And ... it works !

Multi-color cup

From now on, all we need to create a copy with a different color scheme is to write a new class.


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

If you still want a monochrome icon, you do not need to repeat the same color for each CSS variable . Instead, you can define one rule for fill: in this case no CSS variables are defined, the property definition will be used fill.

.icon-monochrome {
  fill: grey;
}

The property fillwill work because the attribute of the filloriginal SVG is specified with undefined CSS variable values.

How to name my CSS variables?


Usually one of two CSS naming methods is used: descriptive or semantic . Descriptive - it means to name a variable by the name of the color itself: if your color #ff0000, you name the variable --red. Semantic - it means to name a variable by its purpose: if you use color #ff0000for the handle of a cup, you name the variable --cup-handle-color.

Perhaps your first wish will be to use a descriptive naming convention. This seems natural, as the color #ff0000can be used for other things besides the cup handle. The CSS variable --redcan also be used for other parts of the icon, which should be red. After all, that's how the utility-first CSS methodology works and it's good..

The problem is that in our case we cannot apply atomic classes to elements that we want to style . The utility-first principles are not applicable, since we only have a link for each icon, and we must stylize it through class variations.

The use of a semantic naming method, such as --cup-handle-color, in our case, is more appropriate. When you need to change the color of some part of the icon, you know exactly what and how you need to redefine. The class name will remain valid no matter what color you have assigned.

Default or non default


A tempting idea is to make your default icons colorful. In this case, you can use them without additional styling, and only if necessary add a separate class.

There are two ways to achieve this: through : root or through var () default .

: root


You can define all your CSS variables in a selector :root. This will keep them all in one place and "share" similar colors. :roothas the lowest specificity, so it is 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, this method has significant drawbacks . For starters, storing color definitions separately from the corresponding icons can be confusing. When you decide to override them, you will have to jump back and forth between the class selector and :root. But, more importantly, this method does not allow you to change your CSS variables , so you cannot reuse the same names.

In most cases, when the icon uses only one color, I name the variable --fill-color. It is simple, understandable and allows you to use this name for all one-color icons. If I define all the variables in the selector :root, I cannot have multiple variables --fill-color. I will have to determine --fill-color-1,--fill-color-2or use namespaces such as --star-fill-color, --cup-fill-color.

var () default


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

my-first-icon

Until you define --color-1, --color-2and --color-3, the icon will use the default values ​​set for each path. This solves the global definition problem that we have in use :root, but be careful: you now have a default value and it does its job . Thus, you can no longer write only one property fillto make the icon monochrome. You need to assign a color to each CSS variable used in the icon individually.

Setting defaults may be useful, but it is a compromise. I suggest not getting used to it, and only do it when it makes sense for a specific project.

How is all of this supported by browsers?


CSS variables are supported by all modern browsers , but, as you probably guessed, IE does not support them at all . Even IE11, and since its development was discontinued in favor of Edge, there is no chance that it will ever support them.

But just because the variables do not work in the browser that you need to support does not mean that you should completely abandon them. In such cases, use graceful degradation : offer colorful icons to modern browsers, and specify a replacement color for old ones.

All you have to do is add a color definition that will only work if CSS variables are not supported. This can be achieved by specifying the propertyfillbackup color. If CSS variables are supported, this declaration will not be taken into account. If this is not so, it will work.

If you use Sass, you can write this check to @mixin.

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

Now you can define color schemes without worrying about browser support.

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

Passing CSS variables in mixinthrough is @contentoptional. If you do this outside, the compiled CSS will be the same. But it can be useful to keep it all in one place.

You can check this example in different browsers. In recent versions of Firefox, Chrome and Safari, the last two cups will be red with gray steam and blue with gray steam, respectively. In Internet Explorer and Edge below version 15, the third icon will be all red, and the fourth will be all blue.

Also popular now: