Brian Mills

Work sample · Documentation

How to hand-build an HTML email that survives Outlook.

A standard operating procedure, written for this portfolio from the process I use in production. It takes you from an empty file to an email that renders correctly in Outlook for Windows, Gmail, and Apple Mail.

Purpose and reader

This guide assumes you can read HTML and CSS but have not built an email before. Follow it in order. Every step exists because skipping it breaks something specific, and each step names what.

Why Outlook is the test

Web browsers render email the way they render websites. Outlook for Windows does not. It renders email with Microsoft Word's engine, which ignores most CSS layout, ignores margin on many elements, ignores background images on most elements, and resizes things on high-DPI screens. So the rule of this whole document is simple: build for Word, and the email will also work in the browsers. The reverse is not true.

Ground rules

  1. Lay out with tables, not divs. Word does not honor CSS positioning, floats, or flexbox. A nested table is the only layout primitive every client respects.
  2. Write every style inline. Some clients strip <style> blocks. The style="" attribute on each element is the only placement that always survives. A <style> block is allowed only as a progressive extra, for mobile tweaks or dark-mode hints, never as the only copy of a rule.
  3. One fixed-width container, 600 pixels. It fits every preview pane and scales down acceptably on phones. Set the width three times: as an HTML width attribute, as inline CSS, and in the Outlook ghost table below. Outlook reads the attribute. Everything else reads the CSS.
  4. Safe fonts only. Web fonts do not load in Outlook. Declare a full stack and accept the fallback: Arial, Helvetica, sans-serif or Georgia, 'Times New Roman', serif. If a design depends on a specific font, the design is wrong for email.
  5. Space with padding on table cells, never margin. Word drops margin unpredictably. Padding on a <td> survives everywhere.
  6. Every image gets four things: a width attribute in pixels, style="display:block" to kill mystery gaps under images, meaningful alt text because many clients block images by default, and an absolute https source.

Build steps

Step 1. The skeleton

Start with the XHTML doctype and the two Microsoft namespaces. Outlook needs them to honor its conditional code later.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:v="urn:schemas-microsoft-com:vml"
      xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta name="x-apple-disable-message-reformatting" />
  <!--[if mso]>
  <noscript><xml><o:OfficeDocumentSettings>
    <o:PixelsPerInch>96</o:PixelsPerInch>
  </o:OfficeDocumentSettings></xml></noscript>
  <![endif]-->
</head>

The PixelsPerInch block stops Outlook from scaling your layout apart on high-DPI Windows machines. The Apple meta tag stops iOS Mail from resizing your text.

Step 2. The preheader

The first text in the body becomes the inbox preview line. Control it, instead of letting "View this email in your browser" be your opening words.

<body style="margin:0; padding:0; background-color:#F1EEE7;">
  <div style="display:none; font-size:1px; line-height:1px;
       max-height:0; max-width:0; opacity:0; overflow:hidden;">
    One sentence that earns the open, under 90 characters.
  </div>

Step 3. The ghost table

Modern clients get a centered container from CSS. Outlook gets the same container from a conditional table only Word can see. They wrap the same content.

<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
  <tr><td align="center">
    <!--[if mso]>
    <table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0"><tr><td>
    <![endif]-->
    <table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0"
           style="width:600px; max-width:600px; background-color:#FFFFFF;">
      <!-- all content rows go here -->
    </table>
    <!--[if mso]>
    </td></tr></table>
    <![endif]-->
  </td></tr>
</table>

role="presentation" on every layout table keeps screen readers from announcing your scaffolding as data.

Step 4. Content rows

Each section of the email is one <tr> in the container. Text lives in a <td> with full inline styling.

<tr>
  <td style="padding:24px 32px 8px 32px;
             font-family:Georgia,'Times New Roman',serif;
             font-size:24px; line-height:30px; color:#1B1B1F;
             mso-line-height-rule:exactly;">
    Section heading
  </td>
</tr>

mso-line-height-rule:exactly forces Word to respect your line height instead of treating it as a minimum. State line-height in pixels, not unitless numbers. Word miscalculates the latter.

Step 5. The bulletproof button

Image buttons vanish when images are blocked. CSS-padding buttons lose their padding in Word. The form that survives everywhere is a small table whose cell is the button.

<table role="presentation" cellpadding="0" cellspacing="0" border="0">
  <tr>
    <td style="background-color:#27405E; border-radius:6px;">
      <a href="https://example.com/" style="display:inline-block;
         padding:13px 28px; font-family:Arial,Helvetica,sans-serif;
         font-size:15px; color:#FFFFFF; text-decoration:none;">Read the issue</a>
    </td>
  </tr>
</table>

Outlook ignores the border-radius. Let it. A square-cornered button that works beats a rounded one that does not.

Step 6. Spacers

Where vertical space is structural, use an explicit spacer row instead of trusting margins.

<tr><td style="height:24px; line-height:24px; font-size:1px;">&nbsp;</td></tr>

Step 7. The footer

Every commercial email needs the sender's physical mailing address and a working unsubscribe link. This is law (CAN-SPAM in the US), not style. Put both in plain small text. A footer also carries the email's identity: who sent this, and why the reader is receiving it.

Testing protocol

Code that looks right is not done. Rendering is the only proof.

  1. Send the build to live accounts in at least Outlook for Windows (the Word engine), Gmail web, and Apple Mail, on phone and desktop both.
  2. In each client check, in order: container width and centering, font fallbacks, image sizing and alt text with images blocked, button shape and tap target, spacing between sections, that links resolve, and dark-mode legibility.
  3. Fix in code, resend, recheck. Never patch one client with a fix you have not retested in the other two. A fix for Outlook can break Gmail.

Common failures and the fix

SymptomCauseFix
Layout explodes only in Outlookdiv or CSS-float layoutRebuild the section as nested tables
Random gaps under imagesimages are inline by defaultdisplay:block on every image
Text huge or tiny on one Windows machineDPI scalingThe PixelsPerInch block from step 1, plus width attributes on tables and images
Spacing collapses in Outlookmargins on text elementsMove all spacing to cell padding or spacer rows
Line spacing wrong only in Outlookunitless or ignored line-heightPixel line-height plus mso-line-height-rule:exactly
Email arrives as a blank gray barthe whole design was one background imageRebuild with real text. Use images to support the text, never to carry the whole message

Pre-send checklist

  • Subject line and preheader written for the reader, not the sender
  • All styles inline. No rule lives only in a <style> block
  • Every layout table has role="presentation"
  • Every image has a width attribute, display:block, alt text, and an https source
  • All links absolute, tested, and tracked where tracking is wanted
  • Physical address and unsubscribe link present and working
  • Rendered correctly in Outlook for Windows, Gmail, and Apple Mail, on phone and desktop
  • Read once aloud, top to bottom, as the recipient

This procedure built Resolution, the newsletter in this section. The newsletter shows the result. This guide shows the method.

I make complicated things make sense.

Next

Back to the work, or read the method behind it under Writing.