HTML validated against common email clients
Why it matters
Email clients are notoriously inconsistent renderers — Outlook 2016-2019 strips flexbox and CSS grid, Gmail removes unreferenced <style> blocks, and Apple Mail handles media queries differently from Yahoo. Templates that render perfectly in the preview pane arrive as broken, unstyled blobs for a meaningful portion of recipients, directly degrading conversion on transactional and marketing sends. The user-experience cost compounds for password resets, receipts, and onboarding flows where a misaligned CTA means an abandoned signup or a support ticket.
Severity rationale
Medium because broken rendering hurts conversion and trust across a large recipient slice without exposing data or breaking delivery.
Remediation
Adopt MJML for new templates so the compiler emits table-based, client-safe HTML, or pipe existing HTML through a CSS inliner like juice before handing it to the ESP. Wire a Litmus or Email on Acid check into CI against Outlook 2016, Gmail web, and Apple Mail. See src/emails/ for template organization.
import mjml2html from 'mjml'
import juice from 'juice'
const { html, errors } = mjml2html(template)
if (errors.length) throw new Error(errors.map(e => e.formattedMessage).join(', '))
const inlined = juice(html)
Detection
-
ID:
email-client-compatibility -
Severity:
medium -
What to look for: Check whether email templates use a framework or toolchain that produces cross-client compatible HTML. Look for MJML (which compiles to email-safe table-based HTML), Foundation for Emails, or a CSS inliner (
juice,inline-css) applied in the rendering pipeline. Check for known incompatible patterns in raw HTML templates:display: flexordisplay: gridwithout table-based fallback,position: absolute, CSS variables, or<style>blocks with no inlining step. Also look for a Litmus or Email on Acid project configuration file (.litmusrc, integration in CI). -
Pass criteria: Email templates use at least 1 framework or tool that produces cross-client compatible HTML (MJML, Foundation for Emails, or a CSS inliner such as
juiceorinline-css). Count all incompatible CSS patterns found in templates (flexbox without table fallback, CSS grid,position: absolute) — there must be 0. Alternatively, a Litmus or Email on Acid configuration is present. -
Fail criteria: Templates are raw HTML with no CSS inlining step and no framework that handles cross-client compatibility. No evidence of email rendering testing tools in the project dependencies. Or incompatible CSS patterns are found.
-
Skip (N/A) when: All email recipients use a known modern client — confirmed by documented client constraints.
-
Detail on fail:
"Templates use flexbox layout without table-based fallback — broken in Outlook 2016-2019 (desktop)"or"No CSS inliner in dependencies and no email framework — <style> blocks will be stripped by Gmail" -
Remediation: Use MJML for email-safe HTML, or add a CSS inliner:
// Using MJML — produces table-based HTML compatible with all clients import mjml2html from 'mjml' const { html, errors } = mjml2html(` <mjml> <mj-body> <mj-section> <mj-column> <mj-text>Hello {{firstName}}!</mj-text> </mj-column> </mj-section> </mj-body> </mjml> `) if (errors.length > 0) { throw new Error(`MJML template errors: ${errors.map(e => e.formattedMessage).join(', ')}`) } // Or use juice to inline CSS into an existing HTML template import juice from 'juice' const inlinedHtml = juice(renderedHtml)After implementing, run client rendering tests — send to a Litmus or Email on Acid account, or preview in Gmail, Outlook, and Apple Mail — to verify the output renders correctly across clients.
Taxons
History
- 2026-04-18·v1.0.0·Initial import from sending-pipeline-infrastructure·automated