I’ve been living a lie

No, really.

This morning I received my copy of Mike Monteiro’s new book, You’re My Favorite Client. So I skipped work (sorry!) and read it.

It is amazing.

The book was written for clients, but I as a developer got so much out of it. If I don’t start making better work from this moment forward, I’m just plain stupid.

I’d quote the book, but I would have to retype the entire book. It’s pure gold. Read a preview on A List Apart, and then buy it.

Grunt development workflow

Ugh, life.

Trying to modernize my development workflow. I sometimes develop my themes in parallel to front-end work, just to make the dev process faster.

Grunt is awesome for that. It compiles my Sass, JS and SVG straight to my theme folder, so no more manual concatenating JS or minifying SVG. Using Grunt also makes it simpler to hand a project over to another developer. They have to run npm install and everything just works.

Please note that I’m not using a local server currently, as I am still manually uploading edited theme files via FTP to the dev server.

So, here we go. My folder structure looks like this:

/css/
    /* All raw sass files */
/js/
    /* All raw js files */
/svg/
    /* All raw svg files */
/theme/
    /inc/
        /* All compiled grunt output files */
    /* All raw theme files */

I could probably toss all the raw css, js and svg folders to an /assets/ folder, but right now it makes sense to me not to do that.

I normally have extra assets that are not touched by Grunt, like fonts, in a subfolder in /inc/.

Let’s go through my Gruntfile.js step by step.

sass: {
    dist: {
        options: {
            style: 'compressed'
        },
        files: {
            'css/global-unprefixed.css': 'css/global.scss'
        }
    }
},
autoprefixer: {
    files: {
        src: 'css/global-unprefixed.css',
        dest: 'theme/inc/global.css'
    }
},

First off is my CSS logic. My SCSS usually lives as partials, these are @imported to my global.scss file. After compiling Sass, Autoprefixer takes over. I have no options set here, since browser support varies greatly per project.

Side note: every time one of my coworkers messes with some experimental css features or prefixing properties in general and they still have issues with getting stuff working, my first question is: “Are you using Autoprefixer?”

concat: {
    files: {
        src: ['js/jquery.min.js', 'js/*.js', 'js/scripts.js'],
        dest: 'theme/inc/global.js',
    }
},
uglify: {
    files: {
        src: 'theme/inc/global.js',
        dest: 'theme/inc/global.min.js'
    }
},

Next up is my JS logic. Most of the project we take on require quite a bit of jQuery use, so that’s included by default. I’m debating myself whether to use the jQuery file that comes with WordPress or control it myself and include it in my global.js. Right now I tend to use my own, so there’s that.

So: my concat task takes all the files in my /js/ folder and concatenates them to my theme folder. It makes sure that jQuery is first and that my scripts.js is last in the concatenated file, with everything else (usually various jQuery plugins) in between.

svgmin: {
    options: {
        plugins: [
        { removeViewBox: false },
        { removeUselessStrokeAndFill: true },
        { removeXMLProcInst:false }
        ]
    },
    dist: {
        files: [{
            expand: true,
            cwd: 'svg/',
            src: ['*.svg'],
            dest: 'svg/min/',
        }]
    }
},
svgstore: {
    options: {
        prefix : 'icon-',
        svg: {
            style: 'display:none;',
            viewBox: '0 0 32 32',
            version: '1.1',
            xmlns: 'http://www.w3.org/2000/svg',
            'xmlns:xlink': 'http://www.w3.org/1999/xlink'
        },
        cleanup: true
    },
    default: {
        files: {
            'theme/inc/global.svg': ['svg/min/*.svg'],
        }
    }
},

Then there’s my SVG icon system compilation logic. I moved away from icon fonts a while back, and currently only support them on old projects. An SVG icon system is a lot better. Icon fonts suffered front cross-browser font rendering issues, but SVG works and looks the same everywhere. That means, IE9+.

I save my SVG files from Illustrator directly to my /svg/ folder. Then svgmin takes control. It removes unnnecessary attributes and fill colors, so I can control the fills through CSS. The minified SVG files are placed in /svg/min/. Then I build an SVG sprite with svgstore and put it in my theme folder, ready to use. There are some necessary attributes that the <svg> element in the sprite gets, in order to get <use> with external resource working.

Now I can use the icons in my templates like this:

<svg>
    <use xlink:href="/inc/global.svg#icon-search">
</svg>

The path is generated by a PHP function, but whatever. SVG icons are so simple to use.

watch: {
    options: {
        livereload: true,
    },
    css: {
        files: 'css/*.scss',
        tasks: ['css'],
    },
    js: {
        files: 'js/*.js',
        tasks: ['js'],
    },
    svg: {
        files: 'svg/*.svg',
        tasks: ['svg'],
    }
}

Then there’s my watch task configuration. Nothing mind-breaking here, either. The task watches my CSS, JS and SVG files and triggers some other tasks on changes.

grunt.registerTask('css', ['sass', 'autoprefixer']);
grunt.registerTask('js', ['concat', 'uglify']);
grunt.registerTask('svg', ['svgmin', 'svgstore']);
grunt.registerTask('build', ['css', 'js', 'svg']);
grunt.registerTask('default', ['build', 'watch']);

And all my Grunt tasks. I put watch in the default task because I more often than not need to watch for file changes. Less characters to type, you know.

I put the base project up on GitHub. Suggestions are welcome.