Etsy Icon>

Code as Craft

Responsive emails that really work main image

Responsive emails that really work

  image

Note: In 2020 we updated this post to adopt more inclusive language. Going forward, we'll use "allowlist/blocklist" in our Code as Craft entries.

If you’ve ever written an HTML email, you’ll know that the state of the art is like coding for the web 15 years ago: tables and inline styles are the go-to techniques, CSS support is laughably incomplete, and your options for layout have none of the flexibility that you get on the “real web”.

Just like everywhere online, more and more people are using mobile devices to read their email.  At Etsy, more than half of our email opens happen on a mobile device!  Our desktop-oriented, fixed-width designs are beautiful, but mobile readers aren’t getting the best experience.

We want our emails to provide a great experience for everyone, so we’re experimenting with new designs that work on every client: iPhone, iPad, Android, Gmail.com, Outlook, Yahoo Mail, and more.  But given the sorry state of CSS and HTML for email, how can we make an email look great in all those places?

Thanks to one well-informed blog commenter and tons of testing across many devices we’ve found a way to make HTML emails that work everywhere.  You get a mobile-oriented design on phones, a desktop layout on Gmail, and a responsive, fluid design for tablets and desktop clients.  It’s the best of all worlds—even on clients that don’t support media queries.

A New Scaffolding

I’m going to walk you through creating a simple design that showcases this new way of designing HTML emails.  It’s a two-column layout that wraps to a single column on mobile:

a new scaffolding

For modern browsers, this would be an easy layout to implement—frameworks like Bootstrap provide layouts like this right out of the box.  But the limitations of HTML email make even this simple layout a challenge.

Client Limitations

What limitations are we up against?

  • Android’s Gmail.app only supports inline CSS in HTML style attributes—no < style> tags, no media queries, and no external stylesheets.
  • Gmail.com supports a limited subset of HTML, stripping out many tags (including all of the tags in HTML5) and attributes (including classes and IDs), and only allows some inline CSS and a very limited subset in < style> tags (more on this later).
  • The iOS and Mac OS X Mail apps support media queries and a large selection of CSS.  Of all clients, these are most like a modern browser.

On to the Code

Let’s start with a simple HTML structure.

< html>
< body>
  < table cellpadding=0 cellspacing=0>
    < tr>
      < td>
        < tablecellpadding=0cellspacing=0>
    < tr>
      < td>
        < div>
          < h1>Header< /h1>
        < /div>
        < div>
          < h2>Main Content< /h2>
          < p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec gravida sem dictum, iaculis magna ornare, dignissim elit.< /p>
          < p>...< /p>
        < /div>
        < div>
          < h2>Sidebar< /h2>
          < p>Donec tincidunt tincidunt nunc, eget pulvinar risus sodales eu.< /p>
        < /div>
        < div>
          < p>Footer< /p>
        < /div>
      < /td>
    < /tr>
    < /table>
    < /td>
    < /tr>
    < /table>
< /body>
< /html>

It’s straightforward: a header and footer with two content areas between, main content and a sidebar.  No fancy tags, just divs and tables and paragraphs—we’re still partying like it’s 1999.  (As we apply styling, we’ll see why both wrapping tables are required.)

Initial Styling

Android is the least common denominator of CSS support, allowing only inline CSS in style attributes and ignoring all other styles.  So let’s add inline CSS that gives us a mobile-friendly layout of a fluid single column:

< html>
< body style="margin: 0; padding: 0; background: #ccc;">
  < table cellpadding=0 cellspacing=0 style="width:100%;">
    < tr>
      < td style="padding: 12px 2%;">
        < table cellpadding=0 cellspacing=0 style="margin: 0 auto; background: #fff; width: 96%;">
          < tr>
            < td style="padding: 12px 2%;">
              < div>
                < h1>Header< /h1>
              < /div>
              < div>
                < h2 style="margin-top: 0;">Main Content< /h2>
                < p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec gravida sem dictum, iaculis magna ornare, dignissim elit.< /p>
                < p>...< /p>
              < /div>
              < div>
                < h2 style="margin-top: 0;">Sidebar< /h2>
                < p>Donec tincidunt tincidunt nunc, eget pulvinar risus sodales eu.< /p>
              < /div>
              < div style="border-top: solid 1px #ccc;">
                < p>Footer< /p>
              < /div>
            < /td>
          < /tr>
        < /table>
      < /td>
    < /tr>
  < /table>
< /body>
< /html>

It honestly doesn’t look that different from the unstyled HTML (but the groundwork is there for your beautiful ideas!).  The table-within-a-table wrapping all the content lets us have our content area in a colored background, with a small (2%) gutter on each side.  Don’t forget the cellspacing and cellpadding attributes, too, or you’ll get extra spacing that can’t be removed with CSS!

Dealing with Gmail

This design is certainly adequate for both mobile and desktop clients, but it’s not the best we can do.  Desktop clients and large tablets have a lot of screen real estate that we’re wasting.

Our main target here is Gmail—desktop and laptop screens keep getting bigger, and we want Gmail users to get a full-width experience.  But Gmail doesn’t support media queries, the go-to way of showing different layouts on different-sized clients.  What can we do?

I mentioned earlier that Gmail supports a small subset of CSS inside

The subset of CSS that Gmail supports is that you are limited to only using tag name selectors—no classes or IDs are supported.  Coupled with Gmail’s limited allowlist of HTML elements, your flexibility in styling different parts of your email differently is severely limited.  Plus, the

The trick is to make judicious use of CSS’s structural selectors: the descendant, adjacent, and child selectors.  By carefully structuring your HTML and mixing and matching these selectors, you can pinpoint elements for providing styles.  Here are the styles I’ve applied to show a two-column layout in Gmail:


< head>
  < style type="text/css">
    /*  1 */    table table {
    /*  2 */      width: 600px !important;
    /*  3 */    }
    /*  4 */    table div + div { /* main content */
    /*  5 */      width: 65%;
    /*  6 */      float: left;
    /*  7 */    }
    /*  8 */    table div + div + div { /* sidebar */
    /*  9 */      width: 33%;
    /* 10 */      float: right;
    /* 11 */    }
    /* 12 */    table div + div + div + div { /* footer */
    /* 13 */      width: 100%;
    /* 14 */      float: none;
    /* 15 */      clear: both;
    /* 16 */    }
  < /style>
< /head>

In the absence of classes and IDs to tell you what elements are being styled, comments are your friend!  Let’s walk through each of these selectors.

Lines 1-3 lock our layout to a fixed width.  Remember, this is our style for Gmail on the desktop, where a fluid design isn’t our goal.  We apply this to the inner wrapping table so that padding on the outer one remains, around our 600-pixel-wide content.  Without having both tables, we'd lose the padding that keeps our content from running into the client's UI.

Next, we style the main content.  The selector on line 4, reading right to left, finds a div immediately following another div, inside a table.  That actually matches the main content, sidebar, and footer divs, but that’s OK for now.  We style it to take up the left two thirds of the content area (minus a small gutter between the columns).

The selector on line 8 styles the sidebar, by finding a div following a div following a div, inside a table. This selects both the footer and the sidebar, but not the main content, and overrides the preceding styles, placing the sidebar in the right-hand third of the content.

Finally, we select the footer on line 12—the only div that follows three others—and make it full-width.  Since the proceeding selectors and styles also applied to this footer div, we need to reset the float style back to none (on line 14).

With that, we have a two-column fixed layout for Gmail, without breaking the one-column view for Android.

The styles we applied to the outer wrapping table keep our content centered, and the other inline styles that we didn't override (such as the line above the footer) are still rendered.

For Modern Browsers

Finally, let’s consider Mail.app on iOS and Mac OS X.  I’m lumping them together because they have similar rendering engines and CSS support—the media queries and full CSS selectors you know and love all work.  The styles we applied for Gmail will be also applied on iPhones, giving a mobile-unfriendly fixed-width layout.  We want Android’s single-column fluid layout instead.  We can target modern, small-screen clients (like iPhones) with a media query:

/* Inside < style> in the < head> */
@ media (max-width: 630px) {
  table table {
    width: 96% !important;
  }
  table div {
    float: none !important;
    width: 100% !important;
  }
}

These styles override the earlier ones to restore the single-column layout, but only for devices under 630 pixels wide—the point at which our fixed 600-pixel layout would begin to scroll horizontally.  Don't forget the !important flag, which makes these styles override the earlier ones.  Gmail.com and Android will both ignore this media query.  iPads and Mail.app on the desktop, which are wider than 630 pixels, will also show the desktop style.

This is admittedly not the prettiest approach. With multiple levels of overriding selectors, you need to think carefully about the impact of any change to your styles.  As your design grows in complexity, you need to keep a handle on which elements will be selected where, particularly with the tag-only selectors for Gmail.  But it’s nearly the holy grail of HTML email: responsive, flexible layouts even in clients with limited support for CSS.

The biggest caveat of this approach (besides the complexity of the code) is the layout on Android tablets: they will display the same single-column layout as Android phones.  For us (and probably for you, too), Android tablets are a vanishingly small percentage of our users.  In any case, the layout isn’t unusable, it’s just not optimal, with wide columns and needlessly large images.

Bringing it All Together

You can find the complete code for this example in this gist: https://gist.github.com/kevingessner/9509148

You can extend this approach to build all kinds of complex layouts.  Just keep in mind the three places where every item might be styled: inline CSS, tag-only selectors in a

I hope that in the future, Gmail on the web and on Android will enter the 21st century and add support for the niceties that CSS has added in recent years.  Until then, creating an HTML email that looks great on every client will continue to be a challenge.  But with a few tricks like these up your sleeve, you can make a beautiful email that gives a great experience for everyone on every client.

You can follow me on Twitter at @kevingessner

Want to help us make Etsy better, from email to accounting? We're hiring!