Last week we were asked to apply HTML styles to the emails on one of our clients applications. It was the first time I had to do something like that and it certainly has some tricks you’ve got to know to get it right. Here’s how we did it.
“Applying HTML styles to an email” is one of the tasks that makes frontend developers grin. If you are not too experienced at frontend, it can be a bit of a hell. Luckily for us Rails developers, Roadie helps a lot.
The problems
To put it simply, the problem when applying styles to an email is that you can’t use an external CSS file as you would normally do. So you have to use inline CSS styling, which is not only very ugly code to read (as if it weren’t enough!), but it is harder to maintain.
To add a bit more pain, each email application has a different way of rendering emails, so, in the same way you have to do “hacks” to get something working properly on Internet Explorer, you need to do some “hacks” to get your emails seen properly on Gmail, Thunderbird, Lotus Notes, or your Android smartphone email app.
The solutions
For the inline CSS problem, we use the roadie gem. Basically, it takes your email template, the CSS for the email you’ve defined in a separate file, and injects the CSS on the email HTML before the email gets sent. Roadie 3.x is framework agnostic, and you can use it in combination with roadie-rails to use it seamlessly in your Rails projects. Anyway, I had some trouble setting it up and ended up using Roadie 2.4, which is for Rails only (sorry!) and proved to work very well in another client’s application.
To deal with email viewer related problems, HTML Email Boiler Plate is a great head start. It will provide you with an email template and CSS that will save you a lot of headaches, as it covers a lot of common problems.
Getting it done, step by step (kinda)
The first thing would be getting the email layout in place, so:
- Install
roadie
. If you use version 2.4.x, you’ll need zero additional configuration (SO good!). If using version 3.x you’ll need to look the docs to get it working. - Download the Email Boilerplate from the site linked above.
- Separate HTML (I’ve put it into HAML for readability) from the CSS. Put the HAML file in your
app/views/layouts
folder and the CSS on yourapp/assets/stylesheets
folder. - Add
email.css
to the asset precompilation line on yourapplication.rb
. - Tell your mailer, or some of your emails methods, to use the layout you just created. This is very easy and explained on the Rails Guides
- Get your layout to
yield
for the contents in your email views. As you will be working with tables, it could be a good a idea to have the main table in the layout, and the rows corresponding for the header and the footer in the layout as well. In the gist linked before, you can see an example, but it will vary depending on your template. - Adapt your mailer views to work as (or within) a table.
- From this moment on, it’s just a matter of seeing the resulting email, and fixing the styling in your CSS file, until it looks neat. Note that many attributes stablished on the tables in the layout can be extracted to the CSS file, but be careful. Have some reference on table HTML at hand just in case (W3C and Stackoverflow are your friends),
Helpers for your email views
When on step 7, you’ll probably have to integrate a table structure in your email views. I’d recommend you to create an email_helper
to abstract as much as possible the table structure into methods. So if you have to put the email text header into something like this:
%tr#mail-subject
%td
= image_tag "happy-face-small.png" # this varies in each email
%td
= "Hello #{@user.name}!" # this varies for each email
Instead of repeating the row-cell structure in each email view, create a helper that does:
module EmailHelper
def email_header_with_icon(icon, title)
content_tag :tr, id: "mail-subject" do
content_tag(:td, image_tag("#{icon}-small.png")) +
content_tag(:td, title)
end
end
end
And in your view, use this new helper to keep things clean and readable (and why not, testable!):
= email_header_with_icon("happy-face", "Hello #{@user.name}!")
If you are creating a common email helper instead of one for each individual mailer, don’t forget to add helper 'email_helper_name'
just below your layout line in your mailer!
Previewing your emails
We normally use the letter_opener gem, which works very well, although you’ll have to send your emails manually to get them shown in the browser, which is a bit slower.
But, if you are using Rails 4.1, then you are a lucky person! Rails 4.1 has a Mail Previewer feature that will make your email testing and previewing a whole lot easier. Check it out and start using it today if you can!
Anyway, if you use this, I’ve noted that it’s not friendly to the curly syntax in HAML, so, in your layout, things like this:
%table{align: "center", border: "0", cellpadding: "0", cellspacing: "0"}
Should become:
%table(align="center" border="0" cellpadding="0" cellspacing="0")
Nothing some find + replace can’t solve, anyway 😉
Devise and HTML emails
If you are using Devise, probably you have already used rails generate devise:views
to have your Devise views and mailer views available for editing. But now that you have some sexy styling in your emails, you’ll think that the signup emails should get some love too! How can we do this? Add this on your application.rb
inside your application class declaration and then restart your browser:
config.to_prepare do
Devise::Mailer.layout 'email' #specify the layout
Devise::Mailer.helper 'email' #only if needed!
end
Now Devise will use your layout in the emails. Remember to edit the mailer views as you did with yours!
If you want to preview the emails with the Rails 4.1 previewer, create a previewer like this:
class Devise::MailerPreview < ActionMailer::Preview
def confirmation_instructions
Devise::Mailer.confirmation_instructions(User.first, {})
end
def reset_password_instructions
Devise::Mailer.reset_password_instructions(User.first, {})
end
end
As a note, one thing I’ve noticed is that once you add the Devise mailer to your previews, every time you modify a previewer or a mailer method, the previewer will fail to show them and you’ll need to restart your local server in order to see the changes you’ve made.
Conclusion
I hope this helps some fellow devs in the future and makes someone life easier. Any comments or suggestions? Don’t hesitate!
Picture “The Roadie” by Gareth Harfoot on Flickr