HTML Email Toolkit
Build emails that survive Outlook. A live preview gallery, a battle-tested responsive boilerplate, VML bulletproof buttons, responsive + accessibility + deliverability guidance, and the rules for sending rich HTML from Power Automate’s “Send an email (V2)” action.
Status update with a single call-to-action button.
Why email HTML is its own thing
Email clients are 15 years behind browsers. Desktop Outlook on Windows renders with Microsoft Word’s engine — no flexbox, no CSS grid, patchy float, and <style> blocks are often stripped. Treat email like 2009 web: tables for layout, inline CSS for styling, fixed widths.
- Lay out with nested
<table>elements, not<div>+ flex. - Put styles inline (
style="…") — many clients drop<head><style>. - Keep the body ≤ 600px wide; phones scale down, desktops do not stretch.
- Use web-safe fonts (Arial, Helvetica, Georgia) with a stack fallback.
- Always set
role="presentation",cellpadding="0",cellspacing="0",border="0"on layout tables.
Try the template gallery
The gallery at the top of this page previews ready-made templates — notification, approval, receipt, digest, reminder, alert, two-column, hero, newsletter, OTP, calendar invite, survey, and welcome — in a sandboxed frame. Switch to Source to tweak any of them, then copy it straight into your flow.
Responsive boilerplate
A single-column, centered shell that holds up across Outlook, Gmail, and Apple Mail. Drop your content into the inner cell.
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0"
style="background:#f4f5f7;padding:24px 0;">
<tr>
<td align="center">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0"
style="width:600px;max-width:600px;background:#ffffff;border-radius:8px;
font-family:Arial,Helvetica,sans-serif;color:#1a1a1a;">
<tr>
<td style="padding:28px 32px 8px;font-size:20px;font-weight:bold;">
Request received
</td>
</tr>
<tr>
<td style="padding:8px 32px 24px;font-size:14px;line-height:22px;color:#444;">
Hi @{triggerBody()?['name']}, we logged your request and will reply
within one business day.
<br><br>
<!-- bulletproof button -->
<table role="presentation" cellpadding="0" cellspacing="0" border="0">
<tr>
<td bgcolor="#2563eb" style="border-radius:6px;">
<a href="https://flowlibs.com"
style="display:inline-block;padding:11px 22px;font-size:14px;
font-weight:bold;color:#ffffff;text-decoration:none;">
View status
</a>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td style="padding:16px 32px;border-top:1px solid #eee;font-size:12px;color:#888;">
Sent by FlowLibs · This is an automated message.
</td>
</tr>
</table>
</td>
</tr>
</table>Send an email (V2)
- In Office 365 Outlook → Send an email (V2), click the </> (code view) toggle on the Body field before pasting HTML — otherwise the editor escapes your tags.
- Inject dynamic values with expressions inside the markup, e.g.
@{triggerBody()?['name']}or@{outputs('Compose')}. - Set Importance, CC/BCC, and Reply To under *Advanced parameters*.
- Attach files (including a
.icscalendar invite) by passing an array of{ Name, ContentBytes }to the Attachments parameter.
Switch to code view first
If pasted HTML shows up as literal <table> text in the received email, you pasted into the rich-text editor. Toggle </> code view, clear the field, and paste again.
Dynamic rows & tables
For a quick grid use the built-in Create HTML table action. For full control over styling, build rows yourself: Select to shape each row’s HTML, then join() into the body.
<tr><td style="padding:6px 10px;border-bottom:1px solid #eee">@{item()?['Title']}</td>
<td style="padding:6px 10px;border-bottom:1px solid #eee">@{item()?['Status']}</td></tr><table role="presentation" width="100%" cellpadding="0" cellspacing="0">
@{join(body('Select'), '')}
</table>Taming Outlook on Windows
Desktop Outlook (2016–2024 + the classic client) renders with Word, so it needs its own workarounds wrapped in <!--[if mso]> conditional comments. These are invisible to every other client, so you can ship one template that satisfies both.
VML bulletproof button (roundrect)
A plain padded <a> collapses to just the text in Outlook — only the words are clickable and the rounded background disappears. A VML roundrect draws a real button Outlook can render; the !mso block serves the normal HTML button to everyone else.
<!--[if mso]>
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:w="urn:schemas-microsoft-com:office:word"
href="https://flowlibs.com"
style="height:44px;v-text-anchor:middle;width:200px;"
arcsize="14%" strokecolor="#2563eb" fillcolor="#2563eb">
<w:anchorlock/>
<center style="color:#ffffff;font-family:Arial,sans-serif;font-size:14px;font-weight:bold;">
View status
</center>
</v:roundrect>
<![endif]-->
<!--[if !mso]><!-- -->
<a href="https://flowlibs.com"
style="display:inline-block;background:#2563eb;border-radius:6px;padding:12px 24px;
font-family:Arial,sans-serif;font-size:14px;font-weight:bold;
color:#ffffff;text-decoration:none;">View status</a>
<!--<![endif]-->Ghost tables for width & centering
Outlook ignores max-width and margin:0 auto. Wrap fluid content in an mso-only fixed-width table — the “ghost table” — so Outlook gets a hard 600px column while modern clients use the responsive <div>.
<!--[if mso]>
<table role="presentation" align="center" width="600" cellpadding="0" cellspacing="0" border="0">
<tr><td>
<![endif]-->
<div style="max-width:600px;margin:0 auto;">
<!-- fluid content here -->
</div>
<!--[if mso]>
</td></tr></table>
<![endif]-->VML background image
<!--[if mso]>
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false"
style="width:600px;height:200px;">
<v:fill type="frame" src="https://cdn.flowlibs.com/hero.png" color="#1e3a8a" />
<v:textbox inset="0,0,0,0">
<![endif]-->
<div><!-- overlay text/buttons here --></div>
<!--[if mso]></v:textbox></v:rect><![endif]-->- Set
mso-line-height-rule:exactly(and an explicitline-height) or Outlook adds extra vertical space. - Use cell
padding, nevermargin— Outlook dropsmarginon most elements. - Add
mso-table-lspace:0pt;mso-table-rspace:0pt;to tables to kill phantom side gaps. - Force the font with an
mso-prefixed fallback; Outlook ignores web fonts entirely. - Apply a page background to both
<body>and the outermost table — Outlook only honours the table.
Conditional comments are mso-only
Anything inside <!--[if mso]>…<![endif]--> renders only in Windows Outlook. Apple Mail, Gmail, and Outlook for Mac/web treat it as a comment and skip it — so keep your real fallback in the !mso branch.
Responsive & mobile
Over half of opens are on a phone. Two strategies: media queries (clean, but ignored by Windows Outlook and stripped by some Gmail contexts) and the hybrid/fluid technique (works almost everywhere because it needs no <style> block).
Mobile stacking with media queries
<style>
@media only screen and (max-width:600px) {
.col { width:100% !important; display:block !important; }
.px { padding-left:20px !important; padding-right:20px !important; }
.h1 { font-size:22px !important; }
}
</style>
<!-- mark the cells you want to stack -->
<td class="col" width="50%" valign="top">…</td>
<td class="col" width="50%" valign="top">…</td>The hybrid (spongy) technique
Instead of media queries, set columns to display:inline-block with a max-width, then wrap each in a ghost table (see the Outlook section). The cells flow side by side when there is room and wrap to a single column when there is not — no <style> required, so it survives clients that strip the head.
| Technique | Outlook (Win) | Outlook.com | Apple Mail | Gmail (web/app) |
|---|---|---|---|---|
<style> + media queries | No | Partial | Yes | Yes (strips some) |
| Background image (CSS) | VML only | Yes | Yes | Yes |
| Web fonts | No (fallback) | No | Yes | No (fallback) |
border-radius | No | Yes | Yes | Yes |
Padding on <a> | Needs VML | Yes | Yes | Yes |
max-width fluid layout | Ghost table | Yes | Yes | Yes |
Design unstacked-first
Because Outlook never stacks, your two-column layout must already look right side by side at 600px. Treat mobile stacking as progressive enhancement, not the baseline.
Images that actually show up
- Host on an HTTPS CDN and link with an absolute URL. Do not embed base64/
ciddata URIs — Outlook strips them and they balloon the message size. - Set explicit
widthandheightattributes so the layout does not jump while images load (and so Outlook reserves the space). - Retina: export at 2× the display size, then constrain with the
widthattribute/style — e.g. a 1200px-wide file shown atwidth:600. - Always give meaningful
alttext; usealt=""for purely decorative images so screen readers skip them. - Most clients block images by default. Style the blocked state — set a
bgcoloron the image cell and a visible text colour so thealttext reads cleanly against it. - Keep critical content (offers, instructions, CTAs) as live text, never baked into an image — image-only emails fail when images are blocked and hurt deliverability.
The base64 caveat
Inlining images as data: URIs feels convenient and renders in this page’s sandboxed preview, but Gmail and Outlook strip or ignore them, and they push you toward the 102KB clip limit. Host real images.
Accessibility
Accessible email is also better email — clearer hierarchy, higher contrast, and content that survives image blocking. A few rules cover most of it.
- Put
role="presentation"on every layout table so assistive tech treats it as a wrapper, not a data grid. - Set a language on the wrapper (
lang="en") so screen readers pronounce content correctly. - Order your source markup in reading order — screen readers and Gmail’s linearizer follow source order, not visual position.
- Give images real
alttext (oralt=""when decorative); never describe a button only by colour. - Hit at least 4.5:1 contrast for body text; do not signal meaning with colour alone (pair it with a label or icon).
- Use
aria-hidden="true"on decorative spacers and glyphs, and write descriptive link text (“View invoice”, not “click here”). - Keep body copy at 14px+ with
line-heightaround 1.5; tiny text is the most common accessibility miss in email.
Preheader does double duty
A good preheader (see Deliverability) is read aloud right after the subject by screen readers — write it as a real summary sentence, not filler.
Dark mode & testing
- Declare
<meta name="color-scheme" content="light dark">and<meta name="supported-color-schemes" content="light dark">so clients know you handled both. - Avoid pure-white logos on transparent PNGs — they vanish on dark backgrounds. Add a subtle stroke/halo, or swap a dark-mode logo via a media query.
- Some clients (Outlook.com, Apple Mail, parts of Gmail) auto-invert colours. Pin critical brand colours and test that inverted text stays legible.
- Watch buttons and bordered callouts — an inverted background can erase a border or wash out white button text.
- Test the real send in Outlook (Windows + web), Gmail (web + mobile app), and Apple Mail, in both light and dark, before shipping a template into production flows.
Deliverability & inbox placement
Rendering is half the battle; the other half is landing in the inbox and reading well in the preview pane.
Preheader text
The preheader is the grey snippet shown after the subject line. If you do not set one, clients grab the first text they find — often “View in browser” or a raw link. Hide a real summary sentence at the very top of <body>, padded so no stray body text leaks in.
<div style="display:none;max-height:0;overflow:hidden;mso-hide:all;
font-size:1px;line-height:1px;color:#ffffff;">
Your request was received — we reply within one business day.
‌ ‌ ‌ ‌ ‌ ‌
</div>- Authentication (SPF, DKIM, DMARC) is DNS/tenant config. With Send an email (V2) your M365 tenant sends, so alignment is usually handled; for custom domains or SMTP, confirm all three pass or you will land in spam.
- List-Unsubscribe: bulk/marketing mail should set the
List-Unsubscribe(and one-click) header — this is not exposed by Send an email V2, so use a sending platform or Graph for true bulk. Always include a visible unsubscribe link too. - Gmail clips messages over ~102KB (“…[Message clipped]”) — keep markup lean, avoid base64 images, and put the important content and unsubscribe link above the cut.
- Avoid spam triggers: all-image emails, ALL CAPS / excessive
!!!, link shorteners, and visible link text whosehrefpoints somewhere else. - Send a plain-text alternative (multipart) — it improves placement and serves text-only clients. Send an email V2 auto-generates one from your HTML; for hand-tuned text use Graph or SMTP multipart.
Tooling & workflow
- MJML — author in a concise, component-based syntax and compile to bulletproof table HTML (it emits the VML/ghost-table boilerplate for you).
- Litmus / Email on Acid — render previews across 90+ real clients and catch Outlook/Gmail breakage before a template hits production flows.
- CSS inliners (Juice, Premailer, or MJML’s built-in inliner) move
<style>rules inline so they survive clients that strip the head. - Plain-text alternative — keep a hand-written
.txtversion of important templates rather than relying solely on auto-generation. - RTL / localization — set
dir="rtl"on the wrapper for Arabic/Hebrew and mirror padding; keep copy as live text (not images) so it can be translated, and setlangper locale.
Build a library, not one-offs
Save one tested base template (boilerplate + bulletproof button + preheader) in your FlowLibs library and clone it per flow. Test once, reuse everywhere — and keep the gallery on this page as your starting points.