MJML Dark Mode Email Guide
A technical reference for building dark-mode-aware MJML templates, inspecting compiled HTML and applying client-specific fallbacks safely.
Generated HTML
Source MJML becomes nested responsive table markup, so dark mode selectors must target the compiled result, not the source tree.
mj-head layer
Use mj-style, mj-raw and head-level declarations for dark CSS, meta tags and client patches.
Compiled checks
Many dark mode bugs only appear after MJML wraps, inlines or restructures your original components.
Different strategies
Apple Mail, Gmail, Outlook.com and classic Outlook Windows need different dark mode fallbacks.
This is an independent MailMode guide, not official MJML, Apple, Google or Microsoft documentation. MJML compiler output, email client behaviour and account-specific support can change, so use these patterns as practical checks rather than universal guarantees.
MJML helps structure email, not dark mode itself
- MJML does not provide native dark mode support or a magic dark mode switch; the hard constraints come from email clients.
- MJML components compile into generated wrappers, tables and inline styles that can change where classes and styles land.
css-classmay be placed on an outer generated node, so inspect the compiled HTML before writing final selectors.- Current MJML can place
css-classfrommj-bodyon the generated body tag, which is useful for Gmail targeting, but this still needs compiled-output verification. - MJML 5 changed body/class placement and minification behaviour, so upgrades should trigger a visual dark mode re-check.
- Dark mode CSS usually belongs in
<mj-style>inside<mj-head>. - Keep media queries and client-targeting selectors in embedded
mj-style, notmj-style inline="inline". - Dark mode meta tags usually need
<mj-raw>inside<mj-head>. - Apple Mail is the strongest target for authored
prefers-color-schemeCSS. - Gmail requires targeted selector patterns like
u + .body, and those do not cover every Gmail account type. - Outlook.com can require
[data-ogsc]and[data-ogsb]reaction selectors. - Classic Outlook for Windows remains the MSO/VML fallback path, not a modern CSS target.
- Use
mj-attributes,mj-classor your build system for colour tokens; runtime CSS variables are not production-safe across Gmail, Outlook and Yahoo. - Always test the compiled HTML, not just the MJML source.
MJML dark mode work happens in the generated HTML
MJML is a source format. The email that Apple Mail, Gmail and Outlook receive is the compiled HTML output, with generated tables, wrapper cells, inline attributes and client fallbacks.
A class added to mj-section, mj-wrapper or mj-button may not sit on the element you expected after compilation. That matters when writing dark mode selectors, Gmail selectors or Outlook reaction selectors.
MJML release changes can alter body structure, class placement and minified CSS output. Treat compiler upgrades like rendering changes: recompile, inspect and visually test again.
Inspect the compiled HTML before writing dark mode selectors. The safest workflow is: write MJML, compile it, inspect the generated selectors, then test the compiled HTML in MailMode and real clients.
MJML tools for dark mode email
MJML behaviour notes are based on official MJML documentation, compiler output considerations, email-client support references and email developer testing sources. Always test compiled HTML because output can vary by MJML version, build setup and email client.
<mj-head>Head containerHead CSSOnly useful if children compile correctly.Place styles, attributes and raw meta tags here.<mj-body css-class>Body-level hookClient-specificCompiler-version behaviour matters.Use for Gmail body-class targeting, then verify the generated body output.<mj-style>Embedded CSSRuntime CSSEmail clients can strip or ignore parts of it.Use for media queries and client selectors.<mj-style inline="inline">Inline selected CSSCompile-timeCan flatten rules that need to remain in the head.Do not inline media queries or Gmail/Outlook targeting rules.<mj-raw>Literal outputHead CSSInvalid placement can break output.Use for meta tags and carefully scoped MSO comments.<mj-attributes>Component defaultsCompile-timeLight defaults can be transformed by clients.Use for baseline colours, spacing and typography.<mj-class>Reusable MJML attributesCompile-timeNot the same as a CSS class.Use as compile-time design tokens, not runtime client targeting.<mj-html-attributes>HTML attributes on generated nodesUse with cautionSelector paths are compiler-dependent.Use only after inspecting output.css-classAdd class to generated markupClient-specificMay land on an outer wrapper.Verify exactly where the class compiles.Declare dark mode support through mj-raw
<mjml>
<mj-head>
<mj-raw>
<meta name="color-scheme" content="light dark">
<meta name="supported-color-schemes" content="light dark">
</mj-raw>
<mj-style>
:root {
color-scheme: light dark;
supported-color-schemes: light dark;
}
</mj-style>
</mj-head>
</mjml>prefers-color-scheme inside MJML
mj-style<mj-head>
<mj-style>
@media (prefers-color-scheme: dark) {
.email-shell {
background-color: #111114 !important;
}
.surface {
background-color: #1b1b22 !important;
color: #f5f5f7 !important;
}
.muted-text div {
color: #b8b8c7 !important;
}
}
</mj-style>
</mj-head>
<mj-body css-class="email-shell" background-color="#f6f4ff">
<mj-section css-class="surface" background-color="#ffffff">
<mj-column>
<mj-text css-class="muted-text">Dark mode-aware copy</mj-text>
</mj-column>
</mj-section>
</mj-body>MJML component dark mode risks
Email shell
Generates: document/body wrapper backgrounds. Risk: client rewrites create unexpected page colour. Safer: set both MJML background and dark CSS fallback.
Section surfaces
Generates: table wrappers and cells. Risk: class may land outside the coloured cell, and Outlook background support has limits. Safer: inspect output before targeting backgrounds.
Grouped panels
Generates: nested wrappers. Risk: inner and outer surfaces can transform differently, causing seams. Safer: give each visible layer explicit colours.
Responsive cells
Generates: table cells and mobile responsive rules. Risk: background-color and inner-background-color can become separate surfaces. Safer: test mobile compiled HTML.
Copy blocks
Generates: an inner div. Risk: dark text on transformed surfaces. Safer: target the generated text element and use readable dark values.
Logos and icons
Generates: linked image markup. Risk: transparent logos lose contrast. Safer: use image swaps, padding, outlines or alternate assets.
CTA buttons
Generates: HTML button structures, not a complete bulletproof VML button. Risk: fill, text and border mismatch in dark clients. Safer: test the compiled button and use a dedicated MSO/VML fallback if Outlook desktop is critical.
Hero backgrounds
Generates: complex image/table layout with explicit dimensions. Risk: text over imagery and fallback colours become fragile. Safer: keep text contrast independent from automatic inversion.
Swap logos and critical assets deliberately
<mj-head>
<mj-style>
.dark-logo { display: none; max-height: 0; overflow: hidden; }
@media (prefers-color-scheme: dark) {
.light-logo { display: none !important; max-height: 0 !important; overflow: hidden !important; }
.dark-logo { display: block !important; max-height: none !important; }
}
</mj-style>
</mj-head>
<mj-section>
<mj-column>
<mj-image css-class="light-logo" src="https://example.com/logo-light.png" alt="Brand" width="160px" />
<mj-image css-class="dark-logo" src="https://example.com/logo-dark.png" alt="Brand" width="160px" />
</mj-column>
</mj-section>Target Gmail from compiled MJML carefully
Gmail notes are based on Gmail CSS support documentation and email developer testing references. Gmail Web, Gmail iOS, Gmail Android and Gmail Apps with Non-Google Accounts should be treated as separate test surfaces.
<mj-head>
<mj-style>
u + .body .gmail-surface {
background: #1f1f23 !important;
background-image: linear-gradient(#1f1f23, #1f1f23) !important;
color: #ffffff !important;
}
u + .body .gmail-surface div {
color: #ffffff !important;
}
div > u + .body .gmail-android-fix {
background-color: #1f1f23 !important;
}
</mj-style>
</mj-head>
<mj-body css-class="body">
<mj-section css-class="gmail-surface" background-color="#663399">
<mj-column>
<mj-text>Gmail-targeted content</mj-text>
</mj-column>
</mj-section>
</mj-body>Use Outlook.com selectors and MSO fallbacks separately
Outlook notes distinguish web, mobile, Mac, new Outlook and classic Outlook Windows paths. MSO/VML guidance is for classic Outlook Windows only and should not be applied as a universal Outlook strategy.
<mj-head>
<mj-style>
[data-ogsc] .outlook-surface {
background-color: #1f2937 !important;
color: #ffffff !important;
}
[data-ogsc] .outlook-surface div {
color: #ffffff !important;
}
[data-ogsb] .outlook-button {
border-color: #8b5cf6 !important;
}
</mj-style>
</mj-head>mj-raw<mj-raw>
<!--[if mso]>
<style>
.mso-button { background: #111827 !important; color: #ffffff !important; }
</style>
<![endif]-->
</mj-raw>How MJML dark mode patterns map to clients
Client support describes practical dark-mode strategy, not exact output. CSS support, MJML compiler output and forced dark-mode behaviour are separate concerns that need compiled HTML testing.
prefers-color-scheme and image swaps.Rich HTML, simple mail and OS versions can differ.prefers-color-scheme.Gmail Web, iOS, Android and GANGA differ.data-og* patches and resilient HTML buttons.Not a VML client.Common MJML dark mode failures
Selector misses the visible cell
What breaks: dark CSS targets the wrapper but not the coloured table cell. Safer: inspect compiled HTML and target the generated element.
Body class targeting is missing
What breaks: u + .body never matches. Safer: add css-class="body" to mj-body and verify output.
Dark meta without complete CSS
What breaks: the client is told dark mode exists but surfaces, text or borders are incomplete. Safer: style the full visible component.
Using standards CSS for Outlook reaction bugs
What breaks: rewritten colours are not patched. Safer: use data-og* selectors for tested Outlook.com cases.
Assuming mj-button is bulletproof
What breaks: VML, HTML fill, border and text can diverge. Safer: inspect the compiled button and test the VML branch.
Transparent logo disappears
What breaks: surrounding surface changes but logo pixels do not. Safer: use alternate assets, outlines, padding or non-transparent marks.
Inlining flattens useful rules
What breaks: media queries and client selectors are lost or changed. Safer: keep runtime dark CSS in normal mj-style.
MJML version changes output
What breaks: class placement, minified CSS or body structure changes after an upgrade. Safer: re-run compiled HTML QA whenever the compiler changes.
Manual post-compile edits get lost
What breaks: build output stops matching tested HTML. Safer: automate post-compile patches or keep fixes in MJML source.
A safer MJML dark mode workflow
- Start with a robust light email that remains readable if a client makes no colour changes.
- Keep component defaults in
mj-attributes, but keep runtime dark CSS inmj-style. - Add dark mode meta tags deliberately through
mj-raw. - Use
css-classfor semantic hooks, then verify where those hooks land in the compiled HTML. - Use a body class for Gmail targeting when needed, but confirm how your MJML version emits it.
- Use
prefers-color-schemefor standards-friendly clients, especially Apple Mail. - Use Gmail and Outlook targeting only for their own client families.
- Avoid exact black/white as the only contrast plan; use near-black, off-white and explicit border tokens.
- Prefer MJML compile-time tokens or build-time variables over runtime CSS variables for production email colours.
- Test logos, buttons, borders, cards and footer sections in the compiled HTML.
- Track your MJML compiler version so output changes are visible during QA.
How this maps back to MailMode previews
For MJML templates, compile the MJML first and preview the resulting HTML. MailMode checks the final email output users will send, not the source component tree. For supported inputs and renderer limitations, see the MailMode MJML documentation.
Authored CSS mode helps check intentional dark CSS from mj-style and dark mode declarations from mj-raw.
Partial invert helps approximate selective client adjustments like Outlook.com/mobile or some Gmail Android-style cases where surfaces and text may be changed separately.
Full invert helps stress-test aggressive forced dark behaviour, including the kinds of surface, text, VML/background and button mismatches that can appear in classic Outlook-style rendering.
These preview modes are useful approximations, not exact replicas of every email client or every MJML compilation edge case.
What MJML dark mode guidance cannot guarantee
MJML versions can change markup
Selectors that work today can miss after compiler updates, minification changes, package changes or custom build steps.
Gmail is not one renderer
Gmail Web, iOS, Android and non-Google accounts can support different CSS and transform colours differently.
Outlook is multiple families
Classic Outlook Windows, new Outlook, Outlook.com, Mac and mobile should be tested separately.
Button fallbacks are fragile
Compiled mj-button output is not a guarantee against classic Outlook dark mode mismatches.
Runtime variables are limited
CSS custom properties can be useful in supported clients, but should not be the only colour system for Gmail, Outlook or Yahoo.
No preview is perfect
MailMode can highlight likely issues, but real clients still vary by version, OS, account and remote-content settings.
Sources & Further Reading
Last Updated: Sources Reviewed: