A Designer's Workflow for HTML Emails in a Rails App
You already know why HTML Emails suck, but here's a quick list:
- Testing is arduous
- You can't use divs (Hello tables!)
- You have to inline style everything (really everything)
- When you inline things, they become impossible to update
Hear This: I Fixed it.
The emails that our users at OrgSync receive from our app were getting stale, and no one wanted the task of reconfiguring them because of how much a pain it is to work with HTML emails. We have 27 individual views that generate emails, but those views get populated with content from all over our application--so this isn't the easiest of projects to begin with. Add in the list of things that suck about HTML emails and you get a decent-sized headache.
It took a little bit of setup and testing but now I can say that I've solved HTML emails. I've made it easy to write, test, and deploy. It's almost fun! Okay, let's not go that far, but I do think that the workflow we have is easier, and that makes it less painful. We're going to be able to make our emails more useful to our users because they're easier to work with on our end--and that's what it's all about, right?
So I've got some tips for your next email project and how to get over some of these hurdles:
Tip 1: Use Rails Layouts
I'm not sure when ActionMailer layouts got added to Rails, but I know that it wasn't in the iteration of Rails we were using when our emails were first written. Every view was hand-coded, with the entire template from header to footer embedded around the actual content of the email. For some reason, there seem to be apps that still do it this way, which boggles my mind, and makes me feel better we weren't the only ones behind the times there.
That being said: make the switch if you haven't. Pull out your template code, place it in a proper layout file like you would for anything else in Rails, and it works in the same way. Now when I need to make that change to the background color, I only have to change it in one spot, not 27.
Sidenote: Suck It Up and Write Code Like It's 1998
I didn't solve div support in email clients. I'm sorry if you thought I did; just embrace tables. Give up on trying to get divs to work. Use tables for everything. Use them like you did in 1998 and your email will be drastically more consistent than if you try to make divs work for layout.
Tip 2: Use CSS the Way You Want To
Let's look at an old email in our app. I'm sure you've seen this code before.
<body style="padding: 0px;margin: 0px;font-family: arial, verdana, sans-serif;font-size: 12px;line-height: 1.5em;"> <center> <table width="600" style="font-size: 12px;line-height: 1.5em;"> <tr style="font-size: 12px;line-height: 1.5em;"> <td style="font-size: 12px;line-height: 1.5em;"> <div id="header" style="font-size: 12px;line-height: 1.5em;width: 600px;margin: 0 auto;"> <img src="https://s3.amazonaws.com/newsletter_images/welcome_header.png"> </div><br> </td> </tr> </table> <table width="450" style="font-size: 12px;line-height: 1.5em;"> <tr style="font-size: 12px;line-height: 1.5em;"> <td style="font-size: 12px;line-height: 1.5em;"> <div id="wrapper" style="font-size: 12px;line-height: 1.5em;width: 450px;margin: 0 auto;">
Every line has custom styles written into the HTML. This is the ugliest thing I've ever seen. Seriously. It was impossible to upgrade the look and feel of our emails without manually going through and changing every single line to reflect any new style. You can understand why these emails didn't get changed for so long. But that's how the world worked until Premailer saved the day.
Premailer (Pre-Flight for email) has been packaged as a gem. It does the hard work for you. It lets us write CSS like we normally do, in a CSS file, and then it automatically gets inlined on the fly as the email is generated by our app. While the end-result email looks exactly the same, we don't have to trifle with that madness of code above. We also love the fact we can still write in SASS, so it fits even better into our workflow. You still have to put on your designer hat and know what CSS does and does not work in email clients--but, holy smokes, is this a time saver. Here's a railscast on using the Premailer Gem
- Note: You can't inline media queries, that just doesn't work, but we still wanted responsive emails. Our setup: We have a SASS file that includes all of our base CSS for our emails. Everything in that file will get inlined into the HTML that the email generates. We include that into the layout via a link tag. In that same layout, we've also included a style block with all of our media query specifics. We've told preliner to not inline this block. Email clients that support media queries will still use the declarations in this block; those that don't allow media queries (or style blocks) will ignore them.
After this step is made and premailer is implemented, you're going to find new techniques you can use in your emails and you'll be able to generate markup differently because you won't have to worry about inlining those styles. I owe the premailer people a beer.
Tip 3: Make Testing A Simple Process
The main reason designers hate designing emails is uncertainty. We are sometimes almost guessing as to the outcome of our code on the client side. With the number of emails OrgSync sends out, we had to make testing easier, and it had remove that uncertainty.
Testing functionality - Build a Rake Task
The first thing I want to test is that these emails work; that the data in them is being pulled correctly, that it's making it out of our server and into the real world, and that the basic design looks kosher.
How do you send a test email in your app now? You probably jump into the Rails console and do something like this:
MessageMailer.payment_message(account, organization, amount, payment_type, order_number).deliver
So we're sending a message to a user about the payment they made. Not so bad, it's only one line. Then you realize you have to build all of those objects first in order to pass them to your mailer. Depending on the types of objects we're dealing with, this could be a giant pain. But this is how OrgSync tested emails; if I needed to test this paymentmessage, I would jump in and spend time digging into the code, figuring out what this method was expecting, and finally come up with a working paymentmessage I could send. Then I would exit the console, come back tomorrow and do it all over again. This is dumb. It's even dumber when you multiply that by 27 emails.
Eventually I built myself a text file where I held a lot of these code snippets that set these messages up for me. Also dumb. I was the caretaker of the testing setup; it wasn't in the repository, so no one else knew the information that I had built up.
So I combined those snippets into a simple rake task. It's little more than a task that iterates over each snippet individually and provides a little bit of helpful output. It's a simple hack, but now anyone in the company can test on their own in a safe way.
Here's the setup for that task:
task :send_all => :environment do |acc| # Establish what user we want to send the test to print "\n What is your account id? " acc_num = $stdin.gets.to_i account = Account.find(acc_num) # This is a good spot to do a little input-error checking. I don't trust myself (or my coworkers) # so I ran a check to make sure the account here exists as an administrator in our system # this prevents me from sending our suite of test emails to a random user. # Based on the account, it's also a good idea to establish any other variables that might be used multiple # times by your mailers so you're not establishing them multiple times below. # send a payment message amount = '55' payment_type = 'Credit Card' order = '20000' MessageMailer.payment_message(account, user_group, amount, payment_type, order_number).deliver print "Dues Payment Message Sent\n" ## Now Rinse and Repeat. ## We do this for every message in our system; establish basic defaults, then send a test.
Looking at the payment message again, we already had the account information for whomever initiated the test, and then just created some really simple examples of what those other objects might contain. This will take a little bit of research and app-knowledge on your first go-round of creating them–you need to know what type of objects to return, but this will save time down the road. Imagine all of your emails represented just like payent_message–now, whenever I change the look and feel of the OrgSync emails, all I have to do is run this rake task to see them all show up in my inbox.
If I am just heavily testing one particular email, then I still open up the console and manually run my MessageMailer methods, but now that I have this rake task at least as a snippet directory, I don’t have to re-learn how to create the objects needed for each email.
You’ll probably have common elements between emails, so pull out what you can and define in one spot, in order to DRY up this code.
It’s better to reference real objects in your database rather than objects you build from scratch so that you recreate your app experience (this code was simplified to remove some domain-specific language).
Protip – With every message that goes out, I print out the type of message that was sent so that I can see the progress of the rake task as the emails are being sent, but it’s just as nice to print out a list of every message that wasn’t included in your testing at the very end of this script. I encountered a few emails where tests weren’t really needed, but I also wanted this rake task to include everything that message_mailer.rb defined, for sanity’s sake and for fellow comrades working on this part of the application. When I run this rake task, I know that every email will, at some point, be referenced in this file.
Email Client Support – Use Litmus App
Testing email clients makes the browser wars look like child’s play. On the browser side of things, OrgSync officially supports IE8 , IE9, IE10, FF, Chrome, and Safari — that’s six browsers, and that covers 99.5% of users. My testing list for this project was: Android, Apple Mail 5, Apple Mail 6, Blackberry, Gmail, Hotmail, IPhone 5, IPhone 4, IPhone 3, IPad, Outlook 2003, Outlook 2007, Outlook 2013 and Yahoo Mail–and honestly I could have gone further if I was really trying to cover every base. So we’re looking at 14 different email clients. Note: If you thought Microsoft was dumb for some decisions it made regarding IE, just wait until you dig into Outlook.
[Number of different types of emails we send] x [14 Email Clients] = A bunch of testing
I can take that payment_message we defined above, send it to an email address that litmus provides me with, and, in just a couple of minutes, have a screenshot of what that email looks like in every client. I run through it, put a little checkmark on the clients that pass, and an “x” on what doesn’t. I can quickly see what works and what is going to need a little tender love and care.
The killer feature though is Outlook Live-Editing. You can jump in and live-edit the HTML, change things on the fly, and see how they look in an Outlook client. It’s a slightly dumber version of using a web inspector on a webpage. If you’ve ever sent 30 emails in a row, tweaking individiual CSS attributes to try to fix a bug in Outlook, you can imagine how much time this saves.
Litmus App costs a little bit for a monthly subscription, but I highly recommend it for ease of use and being really good at its job.
The great reward from both Email Layouts and Premailer is the DRY’ing up of our message mailer code. Those views are now a quarter of the code length from where they started before this implementation. Nailing down the right testing methods and building the rake task removes that uncertainty we talked about–uncertainty that a variation of an email was missed or that an email client wasn’t vetted for compatibility; both make me more confident that our users are seeing what I want them to see.
The next thing I really want to do on this project is to combine our two types of testing. Litmus has an API for customers that from the looks of it will be perfect for sending emails via the rake task outlined above, and then we can test our entire suite of emails at once in every client out there.
I’d love any comments you have about the ways you streamlined your processes in regards to email, or suggestions for this process. Just remember: I am a designer, so take it easy when you rip apart my code.
Tyler Lee is a UI Designer / Frontend Developer for OrgSync that tries to do his darndest to make OrgSync more intuitive and easier to use.Follow me on Twitter
comments powered by Disqus