Apple Mail Dark Mode Preview — How It Actually Works
Apple Mail is one of the most developer-friendly email clients when it comes to dark mode — it respects whatever you write in @media (prefers-color-scheme: dark). No forced color inversion, no undocumented overrides. But previewing that in a tool like MailViewr turned out to be tricky, because of a quirk of how iframes work. Here's the problem we ran into and exactly how we solved it.
How does Apple Mail handle dark mode in email?
Apple Mail respects @media (prefers-color-scheme: dark) natively — it never force-inverts colors. To let an in-app toggle control dark mode inside an iframe, you strip or unwrap the media blocks in JavaScript before injecting the HTML. That's exactly how MailViewr's Apple Mail iOS preview works.
Apple Mail Dark Mode Is Nothing Like Gmail
Before getting into the implementation, it's worth understanding why Apple Mail needs a completely different approach from Gmail and Outlook.
Gmail on iOS and Android applies a proprietary CIELAB color inversion algorithm to every email — regardless of what the developer wrote. White backgrounds become dark, light text gets lightened, and colors are shifted based on luminance calculations. Your prefers-color-scheme media queries are ignored entirely.
Apple Mail does the opposite. It respects what you wrote. If you define dark mode styles in a @media (prefers-color-scheme: dark)block, Apple Mail applies them. If you didn't write any dark mode CSS, your email renders as-is — Apple Mail won't touch your colors.
| Email client | Dark mode approach | Developer control |
|---|---|---|
| Gmail iOS / Android | CIELAB L* inversion (forced on all colors) | None — colors overridden regardless |
| Outlook iOS / Android | Partial, undocumented color overrides | Limited |
| Apple Mail (iOS & macOS) | Honors @media (prefers-color-scheme) | Full — renders exactly as authored |
This means Apple Mail rewards the effort of writing proper dark mode CSS. If you do the work, your subscribers see exactly what you designed.
How to Write Dark Mode CSS for Apple Mail
Apple Mail supports @media (prefers-color-scheme: dark) in a <style> block inside the email <head>. Two patterns cover the majority of cases:
Pattern A — Class-based styles (recommended)
If your email uses class names, target them directly. This is the cleanest approach:
<head>
<meta name="color-scheme" content="light dark">
<style>
@media (prefers-color-scheme: dark) {
body { background-color: #0f0a1a !important; }
.email-wrapper { background-color: #1a1330 !important; }
.email-body { background-color: #1f1640 !important; }
p, h1, h2, h3 { color: #ece6f7 !important; }
.btn-primary { background-color: #a78bfa !important; }
}
</style>
</head>Pattern B — Attribute selectors for fully inline-styled emails
Many email templates are sent fully inline-styled (no class names, just style="..." attributes). Since there are no class names to target, use attribute selectors that match on the inline style value:
<style>
@media (prefers-color-scheme: dark) {
[style*="background-color:#ffffff"]
{ background-color: #1a1330 !important; }
[style*="background-color:#f0e8ff"]
{ background-color: #0f0a1a !important; }
[style*="color:#1e0a3c"],
[style*="color:#333333"]
{ color: #ece6f7 !important; }
}
</style>The attribute selector trick works because [style*="..."] does a substring match — it finds any element whose style attribute contains that exact string. The !important is required because inline styles have higher specificity than any class rule.
The Problem: iframes Don't Care About Your Toggle
When MailViewr renders an email preview, it injects the HTML into an <iframe>. This is standard practice — it sandboxes the email so its styles don't bleed into the rest of the app.
The problem is that prefers-color-scheme inside an iframe always reflects the viewer's OS setting. There's no CSS property, no JavaScript API, and no meta tag that lets the parent page override what the iframe sees as the preferred color scheme. The iframe just reads the OS and uses that — end of story.
So when a user clicks the sun/moon toggle in MailViewr on a machine set to Light Mode, the iframe was stuck in light mode regardless. The toggle had no effect. The dark mode CSS blocks inside the email simply never activated.
The Fix: Rewrite the CSS Before Injection
Rather than fighting the iframe's OS inheritance, we rewrite the email's CSS before it goes into the iframe:
- Dark toggle ON — unwrap the
@media (prefers-color-scheme: dark)block so its rules become plain CSS, and strip the light block entirely. - Dark toggle OFF — strip the dark block entirely, and unwrap the light block so its rules become plain CSS.
The result: the iframe always receives plain CSS with no media queries. The “correct” mode is baked in before injection, so the OS setting is irrelevant.
Here's the core function that does this:
function applyPrefersColorScheme(css, isDark) {
const dark = /@media[^{]*prefers-color-scheme\s*:\s*dark[^{]*\{((?:[^{}]*|\{[^{}]*\})*)\}/gi;
const light = /@media[^{]*prefers-color-scheme\s*:\s*light[^{]*\{((?:[^{}]*|\{[^{}]*\})*)\}/gi;
return isDark
? css.replace(dark, '$1').replace(light, '') // force dark
: css.replace(dark, '').replace(light, '$1'); // force light
}Reading the regex once in plain English: it matches @media followed by anything up to the opening brace, then captures everything inside the outer braces — including nested rules in inner {} pairs. The capture group $1 is that inner content. Replacing the whole match with $1 unwraps the block; replacing with an empty string removes it.
Both the extracted <style> content from the email and any separately passed CSS string go through this function before the full HTML document is assembled for the iframe.
This Only Runs for Apple Mail iOS
The rewriting is gated by a single condition:
const honorsPrefersColorScheme = preset.client === 'apple-mail' && isMobile;Gmail iOS still runs through the CIELAB color inversion pipeline as before. Outlook still runs through its own transform. The Apple Mail path is isolated — adding it introduced zero risk of regression to existing clients.
The macOS Desktop preset is intentionally left as-is: desktop renders at the OS setting, which is usually what a macOS user expects when checking their email on their own machine.
Try Apple Mail dark mode preview
Open MailViewr, select Apple Mail — iOS Mobile from the device selector, paste an email with @media (prefers-color-scheme: dark) styles, then toggle the moon icon. Your dark mode styles will activate regardless of your OS setting.
