Dark mode, and the emails that render white on white because of it
Mailclutch now follows your system theme. The harder half was the preview: a message whose dark-mode styles assume the client paints a dark canvas renders invisibly in a light reading pane, so we rewrite its colour-scheme queries instead of trusting them.
Mailclutch follows your system theme now. That part took an afternoon. The interesting half is what a dark operating system does to the email you are previewing, which took considerably longer and is worth writing down, because it is a bug you can hit in your own tooling.
The failure
Some senders support dark mode with a stylesheet that looks roughly like this:
<style> @media (prefers-color-scheme: dark) { .dark-force div { color: #FFFFFF !important } } </style>
No background is set. That is not sloppiness — it is the convention. The sender is trusting the mail client to paint a dark canvas underneath, and turning the text white to sit on it.
Our preview paints white, the way a reading pane does. And here is the part that surprises people: the preview is a nested browsing context, and unless the browser propagates the frame's colour scheme downward, prefers-color-scheme inside the message still answers with the operating system's preference, not the frame's.
So on a machine set to dark: white canvas, white text, !important. The message reads as empty with a stray image in it, and nothing about the page suggests the preview is the thing that is wrong.
Rewriting the query instead of trusting it
The fix is to resolve the message's colour-scheme branches against the canvas it is actually being painted on, before it renders.
Every prefers-color-scheme query inside a <style> element — and in the media attribute that guards the sheet — is rewritten. A dark query becomes a query that can never match; a light query becomes one that always does. Both stay syntactically valid, which is the constraint that rules out simply deleting them: an unparsable media query takes its entire block down with it, so a message would lose rules it needed rather than rules it should not have had.
Two details fall out of doing it as a rewrite rather than a strip. not (prefers-color-scheme: dark) keeps inverting correctly, because it is still a real query with a real answer. And the bare boolean form — (prefers-color-scheme) with no value, which only asks whether the browser knows about colour schemes at all — stays true, because it is true.
Only <style> elements are touched. An inline style attribute cannot hold a media query, so there is nothing to do there, and a message that never mentions a colour scheme is returned untouched — which is the overwhelming majority of mail.
Before the frame paints, a reset declares color-scheme: light and a white background on the document inside it, so form controls and scrollbars are drawn on the canvas the message expects rather than the one your laptop is set to.
Why this belongs in a sandbox
You cannot see this bug in your own inbox, because your own client gets it right — it paints the dark canvas the sender assumed. You see it in a preview, an admin panel, a screenshot tool, a client-approval page: anywhere HTML written for a mail client is rendered somewhere that is not one.
Which is the general argument for looking at your mail somewhere neutral before it ships, the same one behind testing transactional email in staging. A captured message shown correctly is also the precondition for the rest of the analysis being meaningful — the deliverability score is about the message, and so is the preview above it.
Nothing to turn on. The app follows your system theme, and the preview quietly disagrees with it on your behalf. Start from the quickstart if you have not sent anything yet.