πŸ“œ ⬆️ ⬇️

How do I use SVG sprites

Hi, developer!
When layout from PSD, icons are often inserted in SVG format. And if not - I ask them from the designer. Earlier, I used icon fonts, but recently I saw the advantages of sprites and decided to try to play with them injecting them into the development process. I like icon fonts, but they have a number of flaws (read CSSTricks on this topic). The experiment was a success, and that's how I organized the system.

Conditions


What do I want to get from sprites:
  1. Flexible management of size, color and behavior (hover, focus etc) icons
  2. Automation, minimum manual work
  3. Caching icons for good page loading speed
  4. Easy insertion of icons into page markup (I use jade for templating html)


My folder structure:
β”œβ”€β”€ gulpfile.js # gulpfile └──assets #    └── jade/ #  html └── sass/ #  └── js/ #  └── i/ # ,       └──dist #     


Learn more about how my build works - you can read in the repository .
To create a sprite, use gulp, namely:

')

Go!


Install the plugins (I do it globally and then link it):
 npm install gulp-svg-sprites gulp-svgmin gulp-cheerio gulp-replace -g npm link gulp-svg-sprites gulp-svgmin gulp-cheerio gulp-replace 

In the gulpfile declare plugins:
 var svgSprite = require('gulp-svg-sprites'), svgmin = require('gulp-svgmin'), cheerio = require('gulp-cheerio'), replace = require('gulp-replace'); 


Cook sprite


The first task is to create an html file with symbol tags.
 gulp.task('svgSpriteBuild', function () { return gulp.src(assetsDir + 'i/icons/*.svg') // minify svg .pipe(svgmin({ js2svg: { pretty: true } })) // remove all fill and style declarations in out shapes .pipe(cheerio({ run: function ($) { $('[fill]').removeAttr('fill'); $('[style]').removeAttr('style'); }, parserOptions: { xmlMode: true } })) // cheerio plugin create unnecessary string '>', so replace it. .pipe(replace('>', '>')) // build svg sprite .pipe(svgSprite({ mode: "symbols", preview: false, selector: "icon-%f", svg: { symbols: 'symbol_sprite.html' } } )) .pipe(gulp.dest(assetsDir + 'i/')); }); 

Let's see what is happening here in parts.
We say where we need to get the icons and minify them. The variable assetsDir is for convenience.
 return gulp.src(assetsDir + 'i/icons/*.svg') // minify svg .pipe(svgmin({ js2svg: { pretty: true } })) 

Remove the style and fill attributes from the icons so that they do not interrupt the styles specified via css.
 .pipe(cheerio({ run: function ($) { $('[fill]').removeAttr('fill'); $('[style]').removeAttr('style'); }, parserOptions: { xmlMode: true } })) 

But I noticed one bug with this plugin - sometimes it converts the character '>' to the encoding '& gt;'.
This problem is solved by the following piece of task:
 .pipe(replace('>', '>')) 

Now we will make a sprite from the resulting one and put it in a folder:
 .pipe(svgSprite({ mode: "symbols", preview: false, selector: "icon-%f", svg: { symbols: 'symbol_sprite.html' } } )) .pipe(gulp.dest(assetsDir + 'i/')); 

symbol_sprite.html is our sprite. Inside it will contain the following (for simplicity, I have a couple of icons):
 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="0" height="0" style="position:absolute"> <symbol id="icon-burger" viewBox="0 0 66 64"> <path fill-rule="evenodd" clip-rule="evenodd" d="M0 0h66v9H0V0zm0 27h66v9H0v-9zm0 27h66v9H0v-9z"/> </symbol> <symbol id="icon-check_round" viewBox="-0.501 -0.752 18 18"> <path d="M8.355 0C3.748 0 0 3.748 0 8.355s3.748 8.355 8.355 8.355 8.355-3.748 8.355-8.355S12.962 0 8.355 0zm0 15.363c-3.865 0-7.01-3.144-7.01-7.01 0-3.864 3.145-7.007 7.01-7.007s7.01 3.144 7.01 7.01-3.146 7.007-7.01 7.007z"/> <path d="M11.018 5.69l-3.9 3.9L5.69 8.165c-.262-.263-.688-.263-.95 0-.264.263-.264.69 0 .952l1.9 1.903c.132.13.304.196.476.196s.344-.066.476-.197l4.376-4.378c.263-.263.263-.69 0-.952s-.69-.262-.952 0z"/> </symbol> </svg> 

Pinch styles


Now we need to make styles for our sprite (in this case the .scss file). In the gulp-svg-sprites plugin, we can specify this file, but what a nuisance - it cannot be done with this setting:
 mode: "symbols" 

I decided to create a separate task for creating scss. If you find another solution, write in the comments.
 // create sass file for our sprite gulp.task('svgSpriteSass', function () { return gulp.src(assetsDir + 'i/icons/*.svg') .pipe(svgSprite({ preview: false, selector: "icon-%f", svg: { sprite: 'svg_sprite.html' }, cssFile: '../sass/_svg_sprite.scss', templates: { css: require("fs").readFileSync(assetsDir + 'sass/_sprite-template.scss', "utf-8") } } )) .pipe(gulp.dest(assetsDir + 'i/')); }); 

In the cssFile property we declare where to put the file on the scss (then we include it).
In the templates property we declare where to get a template for it. My template code:
 .icon { display: inline-block; height: 1em; width: 1em; fill: inherit; stroke: inherit; } {#svg} .{name} { font-size:{height}px; width:({width}/{height})+em; } {/svg} 

We get the _svg_sprite.scss as follows:
 .icon { display: inline-block; height: 1em; width: 1em; fill: inherit; stroke: inherit; } .icon-burger { font-size:64px; width:(66/64)+em; } .icon-check_round { font-size:18px; width:(18/18)+em; } 

The compiled css will be like this:
 .icon { display: inline-block; height: 1em; width: 1em; fill: inherit; stroke: inherit; } .icon-burger { font-size: 64px; width: 1.03125em; } .icon-check_round { font-size: 18px; width: 1em; } 

Please note that the sizes of the icons are expressed in em , which will allow us to further manage them through font-size.
We make the final task to run one command:
 gulp.task('svgSprite', ['svgSpriteBuild', 'svgSpriteSass']); 

We connect to the page


So we got an html-file with icons and a scss-file with design. Next, connect the file to the page using caching via localStorage. This process is described in detail in the article Caching SVG Sprite in localStorage .
Connect the js-file as follows:
 ;( function( window, document ) { 'use strict'; var file = 'i/symbol_sprite.html', revision = 1; if( !document.createElementNS || !document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ).createSVGRect ) return true; var isLocalStorage = 'localStorage' in window && window[ 'localStorage' ] !== null, request, data, insertIT = function() { document.body.insertAdjacentHTML( 'afterbegin', data ); }, insert = function() { if( document.body ) insertIT(); else document.addEventListener( 'DOMContentLoaded', insertIT ); }; if( isLocalStorage && localStorage.getItem( 'inlineSVGrev' ) == revision ) { data = localStorage.getItem( 'inlineSVGdata' ); if( data ) { insert(); return true; } } try { request = new XMLHttpRequest(); request.open( 'GET', file, true ); request.onload = function() { if( request.status >= 200 && request.status < 400 ) { data = request.responseText; insert(); if( isLocalStorage ) { localStorage.setItem( 'inlineSVGdata', data ); localStorage.setItem( 'inlineSVGrev', revision ); } } } request.send(); } catch( e ){} }( window, document ) ); 

Everything, we connected our file to the page, after the first download it is cached.
I embed icons through mixin jade, because It is fast and convenient:
 mixin icon(name,mod) - mod = mod || '' svg(class="icon icon-" + name + ' ' + mod) use(xlink:href="#icon-" + name) 

Now, in order to embed an icon we call a mixin with its name:
 +icon('check_round','red_mod') +icon('burger','green_mod') 

Result html:
 <svg class="icon icon-check_round red_mod"> <use xlink:href="#icon-check_round"></use> </svg> <svg class="icon icon-burger green_mod"> <use xlink:href="#icon-burger"></use> </svg> 

Open the page in the browser:



While the size of the icons in full size and have a standard color. Let's change this (not in the generated file, but in the main file):
 .icon-burger { font-size:3rem; &.green_mod { fill:green; } } .icon-check_round { font-size:3rem; &.red_mod { fill: red; } } 

Result:

That's it, we got a working system for connecting icons via sprites, but there is one more thing.

Blur


Unfortunately, not all designers make icons on a pixel grid. In this case, the icons will be "blurred." If you export icons from Illustrator, you need to enable the pixel grid and adjust the size and location of the icon to the pixel grid. If you work in ready-made svg-files - use the iconmoon service to properly align them.

Special thanks go to @akella who helped me write this solution.

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


All Articles