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
- 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.
- Write every style inline. Some clients strip
<style>blocks. Thestyle=""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. - 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
widthattribute, as inline CSS, and in the Outlook ghost table below. Outlook reads the attribute. Everything else reads the CSS. - Safe fonts only. Web fonts do not load in Outlook. Declare a full stack and accept the fallback:
Arial, Helvetica, sans-seriforGeorgia, 'Times New Roman', serif. If a design depends on a specific font, the design is wrong for email. - Space with padding on table cells, never margin. Word drops margin unpredictably. Padding on a
<td>survives everywhere. - Every image gets four things: a
widthattribute in pixels,style="display:block"to kill mystery gaps under images, meaningfulalttext because many clients block images by default, and an absolutehttpssource.
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;"> </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.
- 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.
- 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.
- 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
| Symptom | Cause | Fix |
|---|---|---|
| Layout explodes only in Outlook | div or CSS-float layout | Rebuild the section as nested tables |
| Random gaps under images | images are inline by default | display:block on every image |
| Text huge or tiny on one Windows machine | DPI scaling | The PixelsPerInch block from step 1, plus width attributes on tables and images |
| Spacing collapses in Outlook | margins on text elements | Move all spacing to cell padding or spacer rows |
| Line spacing wrong only in Outlook | unitless or ignored line-height | Pixel line-height plus mso-line-height-rule:exactly |
| Email arrives as a blank gray bar | the whole design was one background image | Rebuild 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.