- Version 10.0.1
- Project Flyweb Production
- Section blog
- Tagsnewsletter, css, javascript, grunt 
 Published This Post is a few years old and the Information provided might be out of date!
HTML Email Templates with Zurbs Inky & Grunt
How to easily create responsive HTML email Templates with the Taskrunner grunt

The creation of HTML Emails is still like a journey back to the good ol' nineties when it comes to Layout Programming. When you take a look at the CSS Guide from Campaign Monitor you get a pretty good glimpse at what might or probably might not be supported by the various Email Clients.
One of the best options to keep a sane mind while creating HTML Email Templates is to use one of the available frameworks. Since I use Zurbs Foundation Framework a lot I will describe a setup example with Inky from Zurbs Foundation for Emails and grunt to create templates which will be responsive and should allow a good readability on mobile devices.
From gulp to grunt
The integration of Inky with gulp or as a standalone is already documented at the Inky github page. For an existing project I needed a solution with the grunt taskrunner.
Requirements for the Project
- Use a single Template file to generate custom templates
- Create custom templates with a grunt command grunt watch:email --template='new_email_template'
- Watch for file changes and re/compile the new template automatically
- Parse single templates with inky
- Move extern CSS to <body>element
- Remove unused CSS
- Minify all CSS and HTML Code
- Inline possible CSS code
- Replace placeholders with Backend Templates Tags
Packages
The following node packages are used to get things running:
- matchdep - https://www.npmjs.com/package/matchdep
- siphon-media-query - https://www.npmjs.com/package/siphon-media-query
- foundation-emails - https://github.com/foundation/foundation-emails
- grunt-inky - https://www.npmjs.com/package/grunt-inky
- grunt-inline-css - https://www.npmjs.com/package/grunt-inline-css
- grunt-string-replace - https://www.npmjs.com/package/grunt-string-replace
- grunt-usemin - https://www.npmjs.com/package/grunt-usemin-slashes
Creating HTML Email Templates the easy Way
Assuming you got a project folder, node, grunt and all the required, above mentioned packages are installed, the next step would be to create two files and a sub-folder:
 $ mkdir templates && touch Gruntfile.js master_template.html/Projectfolder
    /templates
    Gruntfile.js
    master_template.htmlGrunt Configuration
The starting point for developing every new HTML Email-Template will be the file master_template.html. This file will be watched for changes by grunt and every time the file is saved, the following will happen:
- The Template file will be created (in the folder /templates) with the filename provided with the grunt command option --template
- The Zurb Inky Custom Tags get converted to HTML
- Extern CSS files will be inlined to the HTML header
- Every possible CSS attribute will be inlined
- Custom Placeholders like <!-- <tracker> -->get replaced with the Backend Templates Tags
- The Browser reloads and the new Template is shown (point the Browser to the template in the /templates folder)
module.exports = function (grunt) {
    require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
    var siphon = require('siphon-media-query');
    var fs = require('fs');
    var nl_css = fs.readFileSync('PATH/TO/foundation-emails.css').toString();
    var mq_css = siphon(nl_css);
    // Filename for created template
    var template = grunt.option('template') || 'default_template_name';
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        watch: {
            email: {
                files: ['master_template.html'],
                tasks: ['email'],
                options: {
                    spawn: true,
                    livereload: true
                }
            }
        },
        inky: {
            base: {
                options: {
                    cheerio: {
                        decodeEntities: false
                    }
                },
                files: [{
                    cwd: '/',
                    src: 'master_template.html',
                    dest: 'templates/',
                    filter: 'isFile',
                    expand: true,
                    rename: function () {
                        return 'templates/' + template + '.html';
                    }
                }]
            }
        },
        inlinecss: {
            main: {
                options: {
                    // applyStyleTags: false,
                    // removeLinkTags: false
                    removeStyleTags: true,
                    preserveMediaQueries: false,
                    webResources: {
                        images: false
                    }
                },
                files: [{
                    expand: true,
                    src: 'templates/' + template + '.html'
                }]
            }
        },
        htmlmin: {
            main: {
                files: [{
                    expand: true,
                    src: 'templates/' + template + '.html'
                }],
                options: {
                    collapseWhitespace: true,
                    minifyCSS: true,
                }
            }
        },
        'string-replace': {
            main: {
                files: [{
                    expand: true,
                    src: 'templates/' + template + '.html'
                }],
                options: {
                    replacements: [{
                        pattern: '<!-- <style> -->',
                        replacement: '<style>' + mq_css + '</style>'
                    },
                    {
                        pattern: '<!-- <tracker> -->',
                        replacement: '{this_is_my_smarty_tag}'
                    }]
                }
            }
        }
    });
    grunt.registerTask('default', []);
    grunt.registerTask('email', ['inky', 'inlinecss', 'string-replace', 'htmlmin']);
};This configuration enables to use the following grunt command:
$ grunt watch:email --template='my_new_template'Layout
Furthermore, instead of creating hundreds of HTML tables for an HTML Email Template, the custom HTML Tags which Zurbs Inky provides will be used to create the Layout:
<table class="row">
  <tbody>
    <tr>
      <th class="small-6 large-6 columns first">
        <table>
          <tr>
            <th>
              <p>Not in a callout :(</p>
            </th>
          </tr>
        </table>
      </th>
      <th class="small-6 large-6 columns last">
        <table>
          <tr>
            <th>
              <table class="callout">
                <tr>
                  <th class="callout-inner secondary">
                    <p>I'm in a callout!</p>
                  </th>
                  <th class="expander"></th>
                </tr>
              </table>
            </th>
          </tr>
        </table>
      </th>
    </tr>
  </tbody>
</table>
<!-- Instead of the tables inky tags can be used -->
<row>
  <columns small="6">
    <p>Not in a callout :(</p>
  </columns>
  <columns small="6">
    <callout class="secondary">
      <p>I'm in a callout!</p>
    </callout>
  </columns>
</row>The Inky documentation provides good examples which Layout elements are supported - https://get.foundation/emails/docs/
Creating the Master HTML EMail Template
With a setup like this I have a default Master Template to start creating new Email Templates. I don't need to worry about any nested HTML tables which makes life a lot easier when building HTML Newsletters.
Master Template Example
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>{subject}</title>
    <meta property="og:title" content="{subject}" />
    <link rel="stylesheet" href="PATH_TO_LOCAL/foundation-emails.css" />
</head>
<style>
body {
    background-color: #fff;
}
a {
    color: #3e86ab;
}
h1 {
    font-weight: bold;
    color: #3e86ab;
}
</style>
<body>
<!-- <style> -->
<table class="body" data-made-with-foundation>
    <tr>
    <td class="float-center" align="center" valign="top">
    <center>
        <container>
            <row>
                <columns>
                    <h3>Lorem ipsum dolor sit amet.</h3>
                    <h1>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Suscipit, dignissimos.</h1>
                    <row>
                        <columns large="4">
                            <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Perspiciatis unde quo, repellendus odit nobis minus optio voluptatibus numquam animi distinctio.</p>
                        </columns>
                        <columns large="8">
                            <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Perspiciatis unde quo, repellendus odit nobis minus optio voluptatibus numquam animi distinctio.</p>
                        </columns>
                    </row>
                </columns>
            </row>
        </container>
    </center>
    </td>
    </tr>
</table>
<!-- <tracker> -->
</body>
</html>