📜 ⬆️ ⬇️

The power of CSS will help you take shape, SVG!


We need:
  1. Set your own icons on the site using SVG.
  2. They should be controlled using CSS (shape, size, fill, effects, including their behavior).
  3. They must be lightweight and in one place to save http requests.
  4. Work in all major modern browsers.

demo

Why am I writing this?


Despite the growing popularity of the vector in the browser, its age and browser support , there are very few good usage decisions, as it seems to me. Let me explain. Of course, they wrote and told a lot about SVG . Even about SVG and CSS together. But when I was faced with the need to make SVG icons for the site, I could not find good flexible solutions. SVG in the browser now looks like a forgotten older person, it's time to comb it, shake it off the dust and send it to the gym. I hope the method described in this article will be useful for someone.

At once I will say yes, I used icon fonts, here are the problems here:
1. Such icons in the browser are rendered as a font and in Windows, for example, soap edges are often obtained. There are CSS properties that should solve this problem, but they only work in WebKit and only under MAC - that is, they are useless. The designer swore.
2. Only in 23 Chrome and only under Windows such a font began to disappear, and in some cases, strongly “tear” the rest of the layout of the site. I used such fonts many times, but for the first time with my own icons. And the first time such a problem.
3. It is impossible to add an internal shadow. In the project it was necessary. The designer swore.
4. The weight of such a font. C SVG does not compare.
5. Still, SVG has more features than the font.

Proceed or baseline data.


The initial data is a clean HTML document with main.css connected to it or the place where we will write the styles of our icons.
Also add the SVG document to the body tag. In it, in the defs section, we will declare the forms of our icons in the form of a clip-path and filters for them (internal shadow).
How to declare SVG is better to look at the harmful old man , since he is the most agile.
')
<!doctype html> <html> <head> <meta charset=utf-8 /> <title></title> <link rel="stylesheet" href="main.css"> </head> <body> <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <defs> <!--            --> </defs> </svg> </body> </html> 


Add form icons


How to get the SVG code, I think you do not need to write? Simply open the SVG file in a text editor or use any vector editor, such as Illustrator. For clarity, here I show only the SVG code from our HTML document (remember that it is in the body tag). Create a clipPath tag and add an SVG form of an icon to it.

 <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <defs> <clipPath id="heart-path"> <path fill-rule="evenodd" clip-rule="evenodd" d="M256,512c0,0-256-144.938-256-311.694C0,29.22,240.62,10.145,256,192 c18.467-181.721,256-162.784,256,8.306C512,367.062,256,512,256,512z"/> </clipPath> </defs> </svg> 


We created a path for an icon in the shape of a heart. Please pay attention to the id = "heart-path" attribute of the clipPath tag.
Through it we will refer to the form of our icon.

Add icons


Insert our icon into the HTML document. It consists of a small SVG document, inside which a square is inserted:

  <svg class="heart-icon icon" viewBox="0 0 512 512"> <rect width="512" height="512" /> </svg> 

Immediately we see a small lack of approach - the icon consists of two elements. The problem, in my opinion, is tolerable since the structure of all the icons is absolutely the same. If anything, you can easily find / replace / remove through the search for the entire project.

Notice the number 512. This is the size of the square in which the icon was inscribed when drawn in the editor. Your icon might be different. The class heart-icon to set the shape of the icon in CSS, the class icon - the unifying class for all icons (will there be mass of them?).

Last step. Adding CSS


Let's see what happened. If you did everything correctly, you will see a black square of 512 by 512 pixels. Malevich already approves. But, I'm afraid, no one will appreciate our manifesto of Suprematism anymore. We continue.

All right, no icons? Getting to the most interesting - CSS.

In main.css which we connected to the document we write styles for the classes heart-icon and icon.

 .icon{ width:32px; height:32px; cursor:pointer; fill: #ccc; } 
Our black square turns gray and 32x32 pixels. The fill property sets the fill of our icon.

 .heart-icon rect{ clip-path:url('#heart-path'); } 
The form of the icon finally appears ! The clip-path is exactly the property that causes the browser to take the form along the # heart-path and apply it to the square.

Add behavior for the icon. These will be: hover,: active states and the checked class.

 .heart-icon rect{ clip-path:url('#heart-path'); } .icon:hover{ fill: #999; } .icon:active, .icon.checked{ fill:red; } 


For clarity, I copied the icon and added a checked class to it.
That's all, our system is ready. Yes, it is simplified for this example. In my project, I would set the color for different icons and, accordingly, the behavior through a separate class. After all, the icons we can have different colors and the behavior can be different (not all the same icons are gray). But do not fill it with a head, let's keep things simple now.

To add a new icon to our system, you need only:

  1. Declare a new clip-path with the shape of the icon and its id in the general SVG document (in the <defs> section).
  2. Create a new class for the shape of the icon in CSS.

So simple.

I added the play icon since its code is very simple and well illustrates that not only paths can be used as a clip-path, but also any shapes (which SVG supports of course).

Add Inner Shadow


For this we will use the SVG filter.
Immediately warn. For this example, I had a little difficulty with icons when using a filter on top of them. Some of them sometimes disappeared. Sometimes. Maybe just me. Be on the alert. And one more problem - so that the filter overlaps nicely, you need to add another element to the icon. Very pity. Now filter icon = 3 elements. So if you need to come up with a trend flat design , feel free to rewind .

Add an item:
  <svg class="heart-icon icon" viewBox="0 0 512 512"> <g> <rect width="512" height="512" /> </g> </svg> 
The <g> tag is the element that is used for the filter.

Add a filter to the SVG section of the document:
 <filter id='inset-shadow'> <!--   --> <feOffset dx='0' dy='0' /> <!--   --> <feGaussianBlur stdDeviation='20' result='offset-blur' /> <!--  drop shadow     --> <feComposite operator='out' in='SourceGraphic' in2='offset-blur' result='inverse' /> <!--    --> <feFlood flood-color='black' flood-opacity='.65' result='color' /> <!--     --> <feComposite operator='in' in='color' in2='inverse' result='shadow' /> <!-- c    --> <feComposite operator='over' in='shadow' in2='SourceGraphic' /> </filter> 

Describe how the filter works, I will not. You can easily read it on the Internet. This is a topic for a separate post. Just cover it with comments.
Note that the filter also has id = 'inset-shadow'

Add a shadow to the icons
 .icon g{ filter:url('#inset-shadow') } 

That's all . I want to write. But that would not be true.

True or love everyone


The fact is that if you open an example in Opera, you will see a set of 3 squares. Those icons that with the shadow disappear altogether and this is the problem of SVG filters. If the browser does not find the filter, it renders the icon transparent, instead of just not applying it.

But why not find it ?! The fact is that 2 browser worlds collide here. Our path url ('# heart-path') Opera takes as a path a relative CSS file or url ('main.css # heart-path') instead of a url ('index.html # heart-path'), as everyone else does. If you set the path exclusively as url ('index.html # heart-path'), then browsers do not parse the SVG document inside index.html, as they consider it to be an external source. Mozilla has the same problems as soon as you move main.css outside the directory with the index.html file. IE also agrees with WebKit on this issue. And WebKit, in turn, is not very friendly with external sources. They need an internal one.

Bred worlds in the corners.


In the sense of peace. That is, we force to drive a round dance.
Yes, we will talk about cross-browser compatibility.

So we need:
  1. Two CSS files with different paths for two different worlds.
  2. Edit and maintain styles of icons in one place (file).
  3. Have one source of declaration of icon forms and filters - one SVG document.
  4. Do not think about it later in the development.

Let's get started In solving these problems, we will be helped by preprocessors. As a CSS preprocessor in this example, I will use Stylus , which is undeservedly deprived of the attention of the Russian-speaking developer community, but will illustrate this example as simply and clearly as possible.
As an HTML preprocessor, I will use PHP, the most popular preprocessor and server language at the same time. Any developer who has ever used a preprocessor will easily write this example for his favorite tool. The main principle.

Let's rename the index.html file to index.php. After that, we will create the / css folder and place the icons.svg file in it, where we will transfer our large SVG document with icon form declarations.



In place of a large SVG document in index.php, we write a PHP expression
<? php include "css / icons.svg"; ?>
which includes the text of the file icons.svg at the place where the expression itself is found.
Point 3 is completed, you can delete.

Now point 1 and 2.

Create the icons.styl file in the / css directory. This will be exactly the one place where we will edit the styles of our icons. Move all the contents of the main.css file into it and design it as a mixin of icons_mixin:
 icons_mixin( path = '' ) .heart-icon rect{ clip-path:url( path + '#heart-path'); } .play-icon rect{ clip-path:url( path + '#play-path'); } .icon{ width:32px; height:32px; cursor:pointer; fill: #ccc; g{ filter:url( path + '#inset-shadow') } &:hover{ fill: #999; } &:active, &.checked{ fill:red; } } 

Mixin takes as a parameter the path to SVG forms. We will use it when creating paths for different browsers.
CSS styles of icons have not changed at all, I just added nesting for the convenience and clarity of the example.

Now create another 2 .styl file. webkit_ie.styl and ff_op.styl.
The first will be used on our website by default, the second only for Mozilla and Opera.

Add to webkit_ie.styl file:
 @import 'icons.styl' icons_mixin() 

We import the file with the mixin of icons and execute it without parameters.
In the file ff_op.styl add:
 @import 'icons.styl' icons_mixin('icons.svg') 

Import the file with the mixin of icons and pass it the path to icons.svg.

Point 1 and 2 are met. Cross out.

If confused, the project looks like this:


In index.php we will fix the path to the styles of icons, now this
<link rel = "stylesheet" href = "css / webkit_ie.css">

And at the very end, before the closing </ body> tag, add a script to conditionally add icon styles for browsers with a different engine than WebKit or Trident (IE):

 <script type="text/javascript"> var firefox = navigator.userAgent.indexOf("Firefox") != -1 ; var opera = navigator.userAgent.indexOf("Opera") != -1 ; if ( firefox || opera ) { document.write('<link rel="stylesheet" href="css/ff_op.css">'); } </script> 

That's all. source code

What have we done !?


Let's look back and see what we’ve done. Despite the fact that at first glance, the system may seem too complex, it seems to me that it turned out to be quite flexible and simple (at my second glance) to have the right to life.

All tasks are completed. I will stop here with your permission. Thank you all for your attention and time, I hope you spent it with benefit.

Source: https://habr.com/ru/post/167085/


All Articles