logo-bs logo-dxn logo-odl

SVG Icon Sprites – An Optimized Workflow

We've written recently about Implementing scalable graphics in your web projects - now we're moving onto how to handle multiple vector graphics and optimising your workflow.

Here at JH we love using graphics that outshine all the other pictures on the net, these sexy little graphics are known as scalable vector graphics. We wrote about our love of SVG’s recently, but now we want to extend on that by explaining a technique we’ve been using recently to handle multiple vector graphics. We’ll look at an optimised workflow that automates the process of generating an SVG sprite, a PNG fallback and all of the required CSS for both. The goal is to have a set-up that allows us to re-generate the sprite sheet and re-write all of the CSS with a single command. This will allow us to add additional graphics to it at any point in the future and never have to worry about anything in the CSS.

So why use a sprite anyway?

Now that the ‘mobile-first’ approach to building websites is becoming the norm, we must start thinking about performance as being a requirement, not just a luxury. We want websites to load fast on any device and the most significant barrier to achieving this is the amount of requests a website makes back to the server. Sprites have been used for years on the web as a reliable way of combining multiple images into a single one, this results in a single HTTP request, as opposed to one for every graphic. Without using sprites, even a simple example of a Logo + five social media icons would be six separate requests. That’s six round-trips to the server and back just to get some simple graphics! Clearly this is not good enough for todays mobile web – we want reduce the number of requests wherever possible.

What we’ll achieve:

We’ll have the ability to re-use a single SVG file as the background for multiple elements. Look at the following Facebook & Twitter icons – these are separate elements, but both use the same SVG file

 

So what’s better about this approach?

As mentioned previously, creating sprites is not a new idea. Designers have been creating them (as PNGs or JPGs) manually for years in Photoshop and actually writing the CSS by hand. This is an extreme waste of time and effort (not to mention error-prone) and there are plenty tools at our disposal that can automate the entire process. We want to work with a single directory of SVGs and let a machine create the Sprite and CSS for us. The workflow should be automated, free from risk of errors and easy to work with. Through a little trial and error at JH, we’ve now settled on a particular technique that we’ll outline in the rest of this tutorial.

Setup

We’ll be configuring tasks to be run with Grunt. If you are not familiar with Grunt, or need some information about installing it, please refer back to our previous article about Front-end Automation in which we walk through the process of getting up and running. Before we look at the tools though, we need to create the following directory structure and files.

    /img
    /css
    /svg
      facebook.svg
      twitter.svg
    /scss
      main.scss
    index.html
    Gruntfile.js

Any SVGs that are placed into the svg directory will be compiled into a sprite that will end up in the img directory. An scss file will be created automatically and will contain all of the positioning properties for each graphic. Finally we’ll compile the scss files into a css file and then we’ll be ready to place that file into our webpage.

Installation

Starting from a new directory, install the tools with the following command:

npm install grunt grunt-iconizr grunt-contrib-sass

Configuration

Now in the Gruntfile.js, start with the configuration for the grunt-inconizr task:

module.exports = function (grunt) {

    grunt.initConfig({
        iconizr: {
            options: {
                dims: true,
                common: "svg-icon",
                keep      : false,
                preview   : 'preview',
                padding: 1,
                render    : {
                    css     : false,
                    scss    : '../scss'
                }
            },
            your_target: {
                src      : 'svg',
                dest     : 'img'
            }
        }
    });

    grunt.loadNpmTasks('grunt-iconizr');

    grunt.registerTask('build-icons', ['iconizr']);
};

As you can see, The iconizr task takes quite a bit of configuration (due to the fact it has so many uses) but the important things to note here are:

  1. We’ve configured it to output scss files instead of css via the ‘render` option.
  2. We’ve set dims to true which will ensure the image dimensions are output into the CSS.
  3. The sprite_task section defines where our source SVGs are and also where to place the finished sprite.

On the final line of that example we’ve registered a custom task build-icons – this is what we can run to invoke our task, but first we’ll need at least one SVG to work with. If you’re working along, you can download an svg file from Iconfinder (scroll down for the free ones). Now that everything is configured and you have an SVG file to work with, simply run the following command to compile your first sprite-sheet.

grunt build-sprite

Output

That command will have generated both a icon.svg & icon.png file in the img/icons/ directory. Also, inside the scss directory you should see a number of files. We’re only interested in _icons-svg-sprite.scss though, so feel free to ignore the others for now. If you open up that file (`_icons-svg-sprite.scss`), you’ll see that a short piece of CSS has been written for you. It includes a base .svg-icon class that sets the background image to the sprite path and also includes class names for the Facebook icon we used in the example. This is enough to enable you to use the following markup in your HTML:

<span class="svg-icon svg-facebook svg-facebook-dims"></span>

The cool thing here, is that no matter how many SVGs you end up compiling, this CSS will always be updated to include any new graphics along with their dimensions. This means that even if you change the facebook icon to another size, or colour it won’t matter. All you would need to do is recompile the sprite and everything will still work.

Final step

We have just one final step before this will actually work for us in a project. We need a way of overriding the background url for the class .svg-icon. Currently, with the configuration from above, the css for .sprite would look like this:

.svg-icon {
    background-repeat: no-repeat;
    background-image: url(icons/icons.svg);
}

Which isn’t actually correct, it happens because it uses a relative path based on the img directory. In reality, you’re going to have your own preference for the location of the compiled sprite anyway, so we need a way to specify it. Unfortunately, grunt-iconizr does not currently provide an open for this and we don’t want to have to open up the scss file it created for us and change it manually – that would defeat the whole idea of automation as we’d have to do that every single time. Instead, we could just use some plain old CSS to specify a different path to the svg file – and whilst doing that, we might as well add the path to the PNG fallback as well.  Inside the scss directory, create another file with the named _sprite-fixes.scss. Inside it, we can override the .svg-icon class to fix the path to the sprite and also add the ‘inline-block’ display property (which will allow the use of inline elements like span for the icons).

$spritePath: "../img/icons";
.svg-icon {
    display: inline-block;
    background-image: url("#{$spritePath}/icons.svg");
    .no-svg & {
        background-image: url("#{$spritePath}/icons.png");
    }
}

Now we just need to import the sprite SCSS file, along with this one into the main.scss that we created earlier and configure the grunt task to do the compilation. main.scss

@import "icons-svg-sprite"
@import "sprite-fixes"

Gruntfile.js – including SASS compilation

module.exports = function (grunt) {

    // Project configuration.
    grunt.initConfig({
        sass: {
            dev: {
                files: {
                    'css/core.css': 'scss/main.scss'
                }
            }
        },
        iconizr: {
            options: {
                dims: true,
                common: "svg-icon",
                keep      : false,
                preview   : 'preview',
                padding: 1,
                render    : {
                    css     : false,
                    scss    : '../scss'
                }
            },
            your_target: {
                src      : 'svg',
                dest     : 'img'
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-sass');
    grunt.loadNpmTasks('grunt-iconizr');
    grunt.registerTask('build-icons', ['iconizr', 'sass']);
};

The build-icons task will now create the sprite and its scss file first, then it will compile it into a single CSS file that ends up in the CSS directory. That’s it. With two simple tasks, we now have a workflow that allows us to drop any number of SVG files into a folder, run a single command and have all the work done for us. Winning.

Resources

Looks like there are no related posts

Start the conversation

Whether it’s a new site or an existing one, Jamie’s ready to respond to you on
jamie@wearejh.com or +44(0)115 933 8784