HTML Email Templates with Zurbs Inky & Grunt

How to easily create responsive HTML email Templates with the Taskrunner grunt

Zurb Foundation Screenshot

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

Packages

The following node packages are used to get things running:

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.html

Grunt 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:

  1. The Template file will be created (in the folder /templates) with the filename provided with the grunt command option --template
  2. The Zurb Inky Custom Tags get converted to HTML
  3. Extern CSS files will be inlined to the HTML header
  4. Every possible CSS attribute will be inlined
  5. Custom Placeholders like <!-- <tracker> --> get replaced with the Backend Templates Tags
  6. 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&apos;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>