A workflow for responsive emails using Ink and Grunt


Recently I’ve been having to deal with creating HTML email templates much more than I had in the past. Most of my previous experience wouldn’t go much further than creating simple transactional emails consisting of a simple table enclosing a bunch of text, nothing too fancy.

Unfortunately, when it comes to producing something of a higher standard, such as an appealing newsletter aimed to attract users into your site, or even to convert those visits into sales, you have to go a bit beyond a simple table with text. And it is when you start doing fancy stuff that you realise what a nightmare it is to taylor a proper email template. Most of the techniques you use on a daily basis when creating responsive web apps are invalidated when you realise that email clients are even more anarchic than the earlier versions of Internet Explorer when it comes to parsing and rendering HTML.

But don’t lose faith my friends, luckily enough we live in a golden era in terms of front-end development, an era plenty of libraries, frameworks and tools in general that make our lives so much easier. The purpose of this article is to share my findings in this quest, in the hope that whoever comes next will find a much easier and efficient way to deal with this burden that is HTML email templating. Let’s begin!

Enter Zurb Ink

Ink is a framework that enables you to create responsive HTML emails that will work just fine in most devices and email clients.

This is a big deal. If you are used to working with any popular front-end framework such as Twitter Bootstrap or Zurb Foundation, you will appreciate the wonders of having a proper grid system to help you structure your content in a series of rows and columns. Wouldn’t it be nice to have something similar when developing email templates? Well, now you have Ink!

Not only does Ink provide us with a grid system, but it also support other components such as panels that can be used to highlight content, buttons that will display consistently through most email clients and will enable you to create amazing calls to action. You will also find helpers to toggle visibility, add support for retina images, etc.

In order to start using Ink you just have to import their CSS file into your template’s HTML file. Then you can add your personal styles below or extend the ones provided by Ink. Your email’s head would look something like this:

<head>
[...]
<
link rel="stylesheet" href="ink.css">
<
link rel="stylesheet" href="your_styles.css">
</
head>

After you have included the library you can start making use of it. In the example below we add a row with two columns, a main content column occupying two thirds of the document and a side bar that takes the missing third:

<table class="container">
<
tr>
<
td>

<
table class="row">
<
tr>
<
td class="wrapper">

<
table class="eight columns">
<
tr>
<
td>

Eight Columns

</
td>
<
td class="expander"></td>
</
tr>
</
table>

</
td>
<
td class="wrapper last">

<
table class="four columns">
<
tr>
<
td>

Four Columns

</
td>
<
td class="expander"></td>
</
tr>
</
table>

</
td>
</
tr>
</
table>

</
td>
</
tr>
</
table>

You can explore all the possibilities that Zurb Ink has on offer by visiting their documentation site. Once you have finished creating your amazingly looking email I’ll meet you in the next section.

I have a kick-ass template, now what?

So, you have put in practice all your front-end coding skills and you have developed a HTML page that looks just great. If only it would look like that in an email client…

The bad news is that you still have a fair bit of work to do. For an HTML email to look good across different clients, the safest bet is to inline all of the styles. This is not just to embed the CSS in the template between <style> tags, it goes down to the level of the style attribute in the HTML tags. The safest you can go is to apply all the styles to each tag using the style attribute.

That sounds like a lot of work… isn’t there any good news? Well, there is, and the good news is that you can automate all of it thanks to the invaluable help of GruntJs.

Grunt is a well known Javascript task runner. For the purpose of this article I will assume you are already familiar with it. If that’s not the case, I encourage you to visit their Getting Started tutorial. It is super easy to get it up and running.

We will basically set up a task that consists of three steps:

  1. Strip out the unused CSS code
  2. Embed the remaining CSS in the template
  3. Inline the styles

Let’s set all that up.

Removing the excess of CSS

As with any CSS framework, you will rarely use all the potential of Ink in a single email campaign, so there are a lot of rules that will only add more weight to the email and will never be used.

To fix this we can make use of grunt-uncss. Just like any Grunt extension, we will use node to install it:

npm install grunt-uncss --save-dev

And then add the task to the Gruntfile

grunt.loadNpmTasks('grunt-uncss');

The last bit is to configure the blog that will sit inside the initConfig method that will tell uncss how to operate.

uncss: {
dist: {
src: ['source_file.html'],
dest: 'dist/css/tidy.css',
options: {
report: 'min' // optional: include to report savings
}
}
}

In the src array you will specify the files that you want to track CSS usage for. In the case of an email communication it will normally be just one single file. The task will analyse that file and extract only the CSS rules being applied to it. Those rules will be outputted to the dest file.

Making use of the simplified CSS file

In order to embed the newly created, shrank CSS file, we can leverage of the processhtml grunt extension. With this extension we can replace all the development CSS files with the distribution one by just adding some metadata in the HTML file.

npm install grunt-processhtml --save-dev

After installing the module we’ll proceed to create the Gruntfile task,

grunt.loadNpmTasks('grunt-processhtml');

and add the configuration to the initConfig method

processhtml: {
dist: {
files: {
'dist/email.html': ['source_file.html']
}
}
}

Here we are setting the source file to be the same as in the previous task, so what is the difference? We will need to add the build metadata to it so processhtml knows what to do. Let’s have a look at the following block of code:

<head>
<!-- build:css css/tidy.css -->
<link rel=”stylesheet” href=”ink.css”>
<link rel=”stylesheet” href=”your_own_styles.css”>
<!-- /build -->
</head>

By using this metadata comments we are telling the task to grab the link declarations between the build block and replace them by a link to the tidy.css file that we created on the first step.

This will generate a distribution html file that references only styles that are being used by it, no extra garbage added. The only missing part is to inline those styles!

Inlining styles, or creating the big HTML mess

Some email clients like Google’s GMail will strip out any <style> tags they find, so in general it is a good idea to inline as much as you can, which would be everything except for media queries.

Ink provides an online CSS inliner that works great. The only problem is that as of the time of this writing they don’t provide a tool to automate such a process. However they advice to use Premailer, which is the project Ink’s inliner is forked from.

Luckily for us, there is a grunt wrapper that we can easily install, but before you will need to install the Premailer gem in case you don’t have it in your system already:

gem install premailer
npm install grunt-premailer --save-dev

Next up, we can configure the Gruntfile accordingly:

grunt.loadNpmTasks('grunt-premailer');

and finally,

premailer: {
main: {
options: {
verbose: true
},
files: {
'dist/email-inline.html': ['dist/email.html']
}
}
}

Pretty straightforward, we pass the file we got after running the previous task as input and we will get an inlined file as specified in the output. That easy!

Finalising…

Obviously we are missing a final bit, which is to configure the grunt task that will run this whole workflow. We can easily do that in the Gruntfile by adding something like

grunt.registerTask('email', ['uncss', 'processhtml', 'premailer']);

With this in place and once we have an HTML file that we want to minimise and inline we would just need to run the command

$ grunt email

And we would be all set for success!

As a final note, these tools work pretty well but they are not perfect. I strongly encourage you to test the results before sending a communication out to end customers.