Building Dark Mode Emails That Works
HTML email development is famously unpleasant. You are writing table-based layouts in 2026 because Microsoft decided in 2007 that Outlook should use the Word rendering engine instead of a browser engine. That one product decision, made nearly two decades ago, is why email developers are still nesting tables and writing inline CSS by hand.
If it works in a browser, it will probably fail in at least one email client your audience uses. MJML exists to absorb as much of that pain as possible.
What MJML Is
MJML (Mailjet Markup Language) is a framework that compiles to production-ready, table-based, inline-styled HTML. You write clean, readable markup using MJML components — mj-section, mj-column, mj-text, mj-image — and the compiler outputs the nested table soup that email clients actually need.
Install it globally via npm:
npm install -g mjml
Compile a template:
mjml template.mjml -o output.html
A basic template looks like this:
Hello from MJML.
Clean to write. The compiled output is around 200 lines of tables and inline styles that you would never want to maintain by hand.
Why Email Rendering Is Still Broken
Web browsers have largely converged on standards. Email clients have not. The reason is structural: email clients are not browsers. Gmail strips your HTML and renders it inside its own DOM. Outlook on Windows uses the Word rendering engine. Apple Mail uses WebKit. Each one has its own rules about what CSS it supports, what it ignores, and what it actively overwrites.
Flexbox, grid, even basic margin: 0 auto — they all fail unpredictably in the email client your biggest customer segment uses.
The practical result: you have to design for the worst renderer, enhance for the best, and accept that your email will look slightly different in different clients. That is not a bug. That is how email works.
How Email Clients Handle Dark Mode
At least 35% of email opens now happen in dark mode — a figure that has only grown since 2022. The problem is that every major client handles dark mode differently, and several of them handle it badly.
There are broadly three behaviours:
Full colour inversion — the client inverts everything: backgrounds, text, and sometimes images. This is the most invasive approach, and it is used by Gmail on iOS, Outlook 2021 on Windows, Office 365 on Windows, and Windows Mail. If you already designed your email with a dark theme, full inversion will ironically force it to become light.
Partial inversion — the client darkens background colours and lightens text but leaves images largely untouched. This is observed in modern iOS Mail builds and Outlook Mobile.
Respects prefers-color-scheme — the client honours your custom dark mode CSS. Apple Mail, iOS Mail, Outlook 2019 and later, Samsung Mail, and Thunderbird all support @media (prefers-color-scheme: dark). You can define exactly what your email looks like in dark mode in these clients.
The practical implication: dark mode overrides via media queries will not work in Gmail or in old versions of Outlook. Build for progressive enhancement — your email should be readable without any dark mode overrides, and the overrides are a bonus for clients that support them.
Implementing Dark Mode in MJML
Dark mode styles go inside an mj-style block in the mj-head. Use !important on everything — email clients apply their own styles aggressively and !important is the only reliable way to win.
:root { color-scheme: light dark; supported-color-schemes: light dark; } @media (prefers-color-scheme: dark) { body, .mj-body { background-color: #1a1a1a !important; background-image: linear-gradient(#1a1a1a, #1a1a1a) !important; } .mj-section, .mj-section > table { background-color: #242424 !important; background-image: linear-gradient(#242424, #242424) !important; } .mj-text p, .mj-text td, .mj-text div { color: #e8e8e8 !important; } .mj-button a { background-color: #4a9eff !important; color: #ffffff !important; } }
A note on background-image: linear-gradient(...): this is not decorative. Some email clients honour background-color overrides but ignore them when a background image is set. The single-colour linear gradient forces the background to the correct value in those clients.
Also note: mj-class attributes map to the compiled CSS class names, which is what lets you target specific sections. The compiled class for an mj-section is .mj-section, for mj-text it is .mj-text, and so on.
The Outlook Targeting Problem
For Outlook on Windows — which still uses the Word engine — media queries are completely ignored. In 2025 and 2026, you are dealing with dual Outlook pain: the old Word-engine desktop client and the newer Chromium-based Outlook simultaneously. Conditional comments are the standard workaround.
MJML handles a lot of this for you in its compiled output. But when you need to target Outlook specifically — for dark mode background overrides, for example — the [data-ogsc] attribute selector is the correct approach for Outlook.com, which partially inverts colours:
[data-ogsc] .email-container {
background-color: #1a1a1a !important;
}
Logo Switching for Dark Mode
A light logo on a dark background becomes invisible. The standard solution in MJML is a dual-image pattern inside an mj-raw block, using CSS to show and hide each version depending on the colour scheme.
Then in your mj-style block:
@media (prefers-color-scheme: dark) {
.logo-light {
display: none !important;
max-height: 0 !important;
overflow: hidden !important;
}
.logo-dark {
max-height: none !important;
overflow: visible !important;
}
}
The max-height: 0 and overflow: hidden approach on the dark logo container is more reliable than display: none alone — some clients ignore display changes but respect height constraints. Using both is the safest approach.
One caveat worth flagging: because prefers-color-scheme has limited email client support, it is worth ensuring the light version of your logo is also legible in clients that do not support dark mode switching — a subtle drop shadow or border can help. For clients that do full colour inversion (Gmail iOS, Outlook Windows), the logo will be inverted automatically regardless, and there is nothing CSS can do about it.
A Complete Dark Mode Template Structure
Putting it together:
Your preview text here. :root { color-scheme: light dark; supported-color-schemes: light dark; } @media (prefers-color-scheme: dark) { body, .mj-body { background-color: #111111 !important; background-image: linear-gradient(#111111, #111111) !important; } .email-body, .email-body > table { background-color: #1e1e1e !important; background-image: linear-gradient(#1e1e1e, #1e1e1e) !important; } .email-text p, .email-text td { color: #e0e0e0 !important; } .logo-light { display: none !important; max-height: 0 !important; overflow: hidden !important; } .logo-dark { max-height: none !important; overflow: visible !important; } } ![]()
Your content here.
Testing
No amount of careful CSS fixes every client. The only reliable way to know what your email looks like is to test in the actual clients. Litmus and Email on Acid both offer preview tools that render your compiled HTML across dozens of client and OS combinations. Apple Mail has the best dark mode support and is a sensible first testing target. Always follow up with Gmail and Outlook to verify nothing breaks in the clients that ignore your media queries entirely.
The goal is not a pixel-perfect dark mode in every client. The goal is an email that is readable and not embarrassing in every client, and looks considered in the ones that support your overrides.