Making Emails React to’s Dark Mode

One thing I love about’s dark mode is the button to toggle between dark mode and light mode for a message. A simple and handy option to quickly change the look of an email. (And I’m excited that a similar button is coming to Firefox DevTools soon.)’s dark mode button with the moon and sun icon.

One thing I hate about this, though, is that if you carefully coded a dark mode for your email using a @media (prefers-color-scheme:dark) media query, that button might do nothing.

That’s because using @media always targets the browser at its highest level. So if you define mobile styles in a @media (max-width:600px) media query, but your email is displayed in a 500px wide preview pane in a webmail inside a 1280px wide browser window, none of those styles will apply.

And if your email is displayed in’s light mode preview pane, but your operating system is set up in dark mode, all of your dark mode styles will still apply. (The problem is very similar to using @supports in an email.)

Here’s an example of such a “problematic” dark mode media query:

@media (prefers-color-scheme:dark) {
    .email-background {
        background-color: #423500 !important;
An example of email with dark modes applied in in both dark and light mode.

In my latest clients work, I tried to find a solution to this.

Excluding from Dark Mode Styles

The first step is to make sure that styles inside a @media (prefers-color-scheme:dark) won’t apply in prefixes class and id attributes in HTML and CSS with an x_.

Using an attribute selector in CSS, we can detect if a class was prefixed by ([class^="x_"]).

Combining this with a :not() pseudo-class and we can now apply styles for an element whose class attribute doesn’t start with x_.

@media (prefers-color-scheme:dark) {
    .email-background:not([class^="x_"]) {
        background-color: #423500 !important;

Adding Dark Mode Specific Styles

The second step is to add back dark mode styles, but this time making them specific for only. This time, we’re going to take advantage of how alternates an email colors for its automatic dark mode.

When it sees an element that doesn’t have “valid contrast”, will fix it by changing either the element’s color and background-color in CSS or color and bgcolor attributes in HTML. What’s a “valid contrast”, you might ask? Well, per’s source code, it’s a color with at least a 4.5 contrast ratio compared to‘s dark background.

And when changes an element’s color, it will add a custom data attribute on said element to save the original color. There are four different possible attributes in total:

  • data-ogsc (for “original style color”)
  • data-ogac (for “original attribute color”)
  • data-ogsb (for “original style background”)
  • data-ogac (for “original attribute background”)

Using once again an attribute selector, we can target when has changed an element’s color and apply our own custom styles. But because only supports attribute selector solo ([attr]) or with an element selector (E[attr]), we can not simply use a selector like .email-background[data-ogsb].

My prefered way to deal with this is to have a main element with a white background color (or any color that gets changed by So then we can use the data-ogsb applied to that main element to target any child element. Here’s an example:

[data-ogsb] .email-background {
    background-color: #423500 !important;

Let There Be Light

And voilà! We now have an HTML email that reacts to’s own dark and light button.

A screenshot of an email in light mode (left) and dark mode (right), both while and the operating system are set up in dark mode.

Here’s the full code for the email above.

I’m aware that this technique might be a bit too hacky and not for every emails. My biggest fear is that this would ultimately remove support for Gmail (if Gmail were ever to support @media (prefers-color-scheme:dark) as rumored almost a year ago).

But for now, I think I like it.