Adding SVG support to the asset pipeline


#2

I have a implementation of this which I run with the Sage gulp tasks.

    gulp.task('svg', function () {
        var svgs = gulp
            .src(path.source + 'svg/*.svg')
            .pipe($.svgmin())
            .pipe($.svgstore({ inlineSvg: true }));

    function fileContents (filePath, file) {
        return file.contents.toString();
    }

    return gulp
        .src(path.source + 'svg/content-svg.php')
        .pipe($.inject(svgs, { transform: fileContents }))
        .pipe(gulp.dest('templates'));
});

This minifies and injects the svgs into the file content-svg.php, which I put in assets/svg. Then just incluide that file in base.php or wherever you want.

/Viktor


#3

@viktor Would you mind elaborating? I’ve added the task you posted above.

Installed and added the vars to the top of my gruntfile.

var svgstore = require('gulp-svgstore');
var svgmin = require('gulp-svgmin');
var inject = require('gulp-inject');

In assets I created an svg directory and inside I saved circle.svg, for example.

assets > svg > circle.svg

Where applicable, I added gulp.watch([path.source + 'svg/**/*'], ['svg']);

Running gulp, content-svg.php does not get compiled to the templates directory.


#4

@djmtype Sure! When I took a closer look, I realised that I should elaborate a bit more

Install the plugins. I’d use gulp-load-plugins and reference the plugins like $.svgmin, $.svgstore etc. If you don’t use gulp-load-plugins, make sure to remove the $. from my code.

Store the svgs in assets/svg directory. In the same directory, create a file called content-svg.php and put

<div style="height: 0; width: 0; position: absolute; visibility: hidden">
  <!-- inject:svg --><!-- endinject -->
</div>

I just use this in the build task, but you could probably use it in the watch task just as well. I’d probably change it to

gulp.watch([path.source + 'svg/*.svg'], ['svg']);

#5

@viktor Thank you! Much appreciated.


#6

Thanks for sharing, @viktor! I’m curious about how you handle fallbacks with this setup, though?


#7

My pleasure!

You can generate a png fallback with https://www.npmjs.com/package/gulp-svg2png. This is my setup:

gulp.task('svgfallback', function () {
    gulp.src(path.source + 'svg/*.svg')
        .pipe($.svg2png())
        .pipe(gulp.dest(path.source + 'svg/fallback'));
});

#8

Cool script @viktor, however, this didn’t work with me :frowning:

[12:13:36] Starting 'svgfallback'...
[12:13:36] 'svgfallback' errored after 6.76 ms
[12:13:36] ReferenceError: $ is not defined

Any idea how to get it to work?


#9

replace $.svg2png() with the variable name you require the plugin with.
For example:

var plugin = require('gulp-svg2png');

gulp.task('svgfallback', function () {
    gulp.src(path.source + 'svg/*.svg')
        .pipe(plugin())
        .pipe(gulp.dest(path.source + 'svg/fallback'));
});

#10

I’m very interested to see an SVG workflow added to Sage. However, I think forcing the SVG sprite into a PHP file is the wrong way to go especially considering there are PNG fallbacks.

I’d rather a root SVG file be in a nondescript file called something like svgs.svg where the name of this file could be modified via the manifest.json file if desired.

It would also be convenient to allow for multiple SVG sprites, maybe in subdirectories? Often in projects, I’ll have icons that should not be loaded universally and are only used on one page but there are enough icons on that one page for them to be a sprite. So I could have two or three SVG sprites in my project that are only loaded as needed on various pages. In essence, any .svg files in the root of assets/svg/ will be sprited and then subdirectories that are created, and the images inside those subdirectories, are also sprited in that subdirectory and the sprite would be named after the subdirectory.


#11

@viktor I’m a little confused on how to display the image in the end, would you mind elaborating?

What I’m trying to do is simply display my logo.svg in my header.php.

I’m fairly new to Sage which is likely why I’m confused in the first place…

Thanks.


#12

Nevermind I figured it out…

For anyone else wondering the same thing, I used the following to use the image in my page.

<? get_template_part('templates/content-svg'); ?>
<svg viewBox="0 0 100 100">
  <use xlink:href="#logo"></use>
</svg>

#13

@shaneparsons And you have to place <? get_template_part('templates/content-svg'); ?>
right after the <body> begins otherwise it won’t work.


#14

Good call,
Mine seemed to work fine when placed under .brand in header.php, however right after <body> in base.php seems a much better place.

thanks.


#15

Hi, I just want to share my svg workflow since it look a little bit different.

I keep all my svg’s in:

assets/images/icons/icon-name.svg

I use svg-injector.js, wich injects the files with JS, and make them cachable. You can install it by running:

bower install svg-injector --save

Sage just imports the script in main.js automagically.

Then I use gulp-svgmin to minify the svg’s, removing comments from sketch/illustrator and such. You can install the plugin with npm like so:

npm install gulp-svgmin --save-dev

Then I make a task in Sage’s gulpfile.js, my task look like this:

// ### Svg
// ´gulp svg´ - Compress SVG files
gulp.task('svg', function () {
  return gulp.src(globs.images)
    .pipe(svgmin())
    .pipe(gulp.dest(path.dist + 'images'));
});

Then offcourse I’ll put it in the images part of the watch task aswell:

gulp.watch([path.source + 'images/**/*'], ['images', 'svg']);

And in the build task:

gulp.task('build', function(callback) {
  runSequence('styles',
              'scripts',
              ['fonts', 'images', 'svg'],
              callback);
});

Then in the main js file you have to initialize the script:

var mySVGsToInject = document.querySelectorAll('img.svg-icon');
    new SVGInjector(mySVGsToInject);

I embed my svg’s like this in the php:

<img class="svg-icon" src="<?php echo get_template_directory_uri(); ?>/dist/images/icons/menu.svg">

Guess you could beautify that with a variable to the path :smile:

Hope it helps!


#16

I almost have exactly the same workflow for SVG’s, except I don’t use svg-injector and I have a separate SVG directory in my assets and dist folder.

A little addition is that I automatically generate fallback png’s of the minified SVG’s for older browser support with a SVG sequence task:

// ### SVG
gulp.task('svg', function(callback) {
  runSequence('svgmin', ['svg2png1x', 'svg2png2x'], callback);
});
// `gulp svgmin` - Minify SVG files
gulp.task('svgmin', function() {
  return gulp.src(path.source + 'svg/*.svg')
    .pipe(svgmin())
    .pipe(gulp.dest(path.dist + 'svg'));
});
// `gulp svg2png` - create fallback png's from SVG assets
gulp.task('svg2png1x', function() {
  return gulp.src(path.dist + 'svg/*.svg')
    .pipe(svg2png(1.0))
    .pipe(gulp.dest(path.dist + 'images'));
});
gulp.task('svg2png2x', function() {
  return gulp.src(path.dist + 'svg/*.svg')
    .pipe(svg2png(2.0))
    .pipe(rename({suffix: '@2x'}))
    .pipe(gulp.dest(path.dist + 'images'));
});

I embed my SVG in a object tag with img fallback like this:

<object type="image/svg+xml" data="<?php echo get_stylesheet_directory_uri(); ?>/dist/svg/logo.svg" class="logo">
  <img src="<?php echo get_stylesheet_directory_uri(); ?>/dist/images/logo.png" srcset="<?php echo get_stylesheet_directory_uri(); ?>/dist/images/logo.png 1x, <?php echo get_stylesheet_directory_uri(); ?>/dist/images/logo@2x.png 2x" alt="Logo">
</object>

I guess this could be done more dynamically, but for now it works perfect!


#18

Hey @samuelhorn, thanks for that info, very helpful.

I’m getting the following error every time I try to run gulp; any ideas?

events.js:154 throw er; // Unhandled 'error' event ^ Error: Error in parsing: Non-whitespace before first tag.

I believe this has something to do with a BOM symbol at the beginning of UTF-8 files (i.e. my svg file)? I have tried many different ways to generate the svg from Illustrator (following guides online etc), but always seem to get this error. Have you come across this, or know of a fix?

Thanks!


#19

Hi,

Anyone tried to do this on sage 9?

I’ve already started without noticing this pull request on sage 8.
I had a problem with the sprite modules for webpack, the ones that existed didn’t work properly. Ended up doing a small custom npm module.

Still missing the whole php part of the pull request. However if anyone already has a solution its better than me redoing everything.


#20

Hi @raramente – I just wondered the same in regards to Sage 9, so I searched the forums high and low and even prematurely posted a similar request here: Best Practice: SVG Wordpress like a Boss

Maybe you could share your solution for Sage 9 or point us to your custom sprite module for webpack?


#21

SVG is well supported now, I don’t think, fallbacks are still required: https://caniuse.com/#feat=svg


#22