Exploring Dark Mode in ⚡AMP Email
Tags: email, ampforemail, html, css
Published: 2022-11-02 00:34:16

## Purpose
This post will investigate potential solutions for dark mode support in [AMP email](https://amp.dev/about/email/).

## Does AMP Email Support Dark Mode?
- **No, there is no dark mode support.** AMP email [does not support](https://amp.dev/documentation/guides-and-tutorials/learn/email-spec/amp-email-css/?format=email#media-features) the `prefers-color-scheme` media feature which is used to apply custom CSS styles for dark mode.
- Also, there are no default dark mode styles that automatically apply to AMP email templates. Users who set "dark mode" in their devices will view the same AMP template without any adjustments to the background color or fonts.

## Main Question
🤔 Despite this current situation, can AMP email still support dark mode?

✅ Yes, by leveraging specific AMP components including `amp-bind`, **light/dark mode color schemes can be selected and applied dynamically within the AMP email template**.

---

### How could this look in 🖥️ Gmail desktop?
![Gmail desktop browser dark mode demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/stf77a272w5equ4d4jon.gif)

### ...and in 📱 Gmail app for iOS?
![Gmail app for iOS dark mode demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wxwhfuypi25istgj0rlt.gif)

---

## 🔩 Creating a Dark Mode Option in AMP Email
The `amp-bind` [component](https://amp.dev/documentation/components/amp-bind/?format=email) allows "elements to mutate in response to user actions or data changes via data binding and simple JS-like expressions".

Through `amp-bind` and the `AMP.setState()` action, users can select a light or dark mode option from a dropdown menu and apply custom CSS styles to the AMP template. Let's begin.

1). Start with the `<select>` HTML element to create a dropdown menu. Define a `value` attribute for each `<option>` tag
```html
<select>
  <option value="lightMode">Light Mode</option>
  <option value="darkMode">Dark Mode</option>
</select>
```

2). Use the `on` attribute to create an [event handler](https://amp.dev/documentation/guides-and-tutorials/learn/amp-email-actions-and-events/?format=email) with a `change` event
```html
<select on="change:">
  <option value="lightMode">Light Mode</option>
  <option value="darkMode">Dark Mode</option>
</select>
```

3). Then add the `AMP.setState()` method to [update the value](https://amp.dev/documentation/components/amp-bind/?format=email#updating-state-variables-with-amp.setstate()) of the `mode` state variable with the option's value when a `change` event occurs
```html
<select on="change:AMP.setState({ mode: event.value })">
  <option value="lightMode">Light Mode</option>
  <option value="darkMode">Dark Mode</option>
</select>
```

4). Create an `<amp-state>` [element](https://amp.dev/documentation/components/amp-bind/?format=email#%3Camp-state%3E-specification) with an `id` of `colorScheme`. Declare JSON data and add state names of `lightMode` and `darkMode` with a state variable of `style`
```html
<amp-state id="colorScheme">
  <script type="application/json">
    {
      "lightMode": {
        "style": "lightStyles"
      },
      "darkMode": {
        "style": "darkStyles"
      }
    }
  </script>
</amp-state>
```

5). Define CSS styles for the `lightStyles` and `darkStyles` classes. These styles will target the background and font colors
```css
<style amp-custom>
  /* LIGHT/DARK MODE STYLES */
  .lightStyles {
    background-color: #ffffff;
    color: #000000;
  }
  .darkStyles {
    background-color: #202124;
    color: #ffffff;
  }
</style>
```

6). Assign the `<body>` element (or the element that wraps your entire email content) with the `[class]` property and bind it to the `<amp-state>` element using the `id` of `colorScheme`. The `colorScheme[mode].style` expression updates the `class` property
```html
<body [class]="colorScheme[mode].style || ''">
```

Through these steps, we can create a CSS class for light mode and a CSS class for dark mode. When the user selects a color scheme, the specific CSS class, properties, and values are immediately applied to the AMP email template ⬇️

![Dark mode simple demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x3s98rrbkdf5e8rmt99x.gif)

---

## 🌙 AMP Email Dark Mode Demo
Below is a demo featuring light/dark mode options. 

> ⚠️ ***NOTE: Copy & paste the HTML tab code into the [Gmail AMP Playground](https://amp.gmail.dev/playground/) or the [amp.dev Playground](https://playground.amp.dev/?mode=Responsive&runtime=amp4email#) for a more accurate demo experience!***

{% codepen https://codepen.io/bdjang/pen/VwxgVKW %}

### 🚧 Areas for Improvement
1. In this specific use case, users must select the dark mode color scheme each time they receive an AMP email. **An improved experience would allow users to select their preferred color scheme once and then apply it to future AMP emails.**
2. In the Gmail app for iOS, there is a noticeable white border when dark mode styles are applied.
![AMP email displayed in Gmail app with unsightly white borders in dark mode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lggh8egp2iagsisk96q4.jpg)
I have not found a way to target and remove these white borders or expand the template's width to cover this area.

### 🌗 Beyond Light and Dark Modes
Since we have the freedom and control over defining the `background-color` and `color` values in each CSS class, this opens the door to creating color schemes that go beyond light and dark modes.

Let's add a [new color scheme](https://randoma11y.com/) called "Sunglow" that will apply `background-color: #032932` and `color: #fbe707` to the AMP template ⤵️

```html
<select on="change:AMP.setState({ mode: event.value })">
  <option value="lightMode">Light Mode</option>
  <option value="darkMode">Dark Mode</option>
  <option value="sunMode">Sunglow Mode</option>
</select>
```

```html
<amp-state id="colorScheme">
  <script type="application/json">
    {
      "lightMode": {
        "style": "lightStyles"
      },
      "darkMode": {
        "style": "darkStyles"
      },
      "sunMode": {
        "style": "sunStyles"
      }
    }
  </script>
</amp-state>
```

```css
<style amp-custom>
  /* COLOR SCHEME STYLES */
  .lightStyles {
    background-color: #ffffff;
    color: #1b1b1b;
  }
  .darkStyles {
    background-color: #202124;
    color: #ffffff;
  }
  .sunStyles {
    background-color: #032932;
    color: #fbe707;
  }
</style>
```
![Sun mode AMP demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/snt1cepxwyzvpb4avdv6.gif)

### 🔮 Looking Ahead
**Could providing multiple color schemes with varying contrasts help improve the user experience in AMP emails?** For example, the Twitter app currently provides [two versions of dark mode](https://www.wired.com/story/twitter-dark-mode/): "dim" vs "lights out".

Through the `amp-bind` method, AMP emails could provide different versions of dark mode as well. More research and work needs to be done on this front! ⚗️🔬

---

### One Last (Important) Item: Logos in Dark Mode
A common issue with dark mode involves images such as logos: **when switching color schemes, logos can become obscured or can unintentionally stick out in dark mode**.

![Logos in dark mode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2lfr8zysi3fd9t9lonfc.png)

❓ Is there a way to replace the original logo with a version that is more appropriate for dark mode in AMP email?
✅ Yes, by **using `[hidden]` [expressions](https://amp.dev/documentation/components/amp-bind/?format=email#expressions) and [event triggering](https://amp.dev/documentation/components/amp-bind/?format=email#event-triggering-and-data), the original logo can be replaced with a "dark mode" version**.

*Continuing off of the previous AMP email sample code:*

1). Add both a 🔳 light and 🔲 dark mode logo
```html
<header>
  <amp-img src="https://YOUR-LIGHT-MODE-LOGO.png" height="48" width="40" layout="fixed" alt="Light mode logo"></amp-img>
  <amp-img src="https://YOUR-DARK-MODE-LOGO.png" height="48" width="40" layout="fixed" alt="Dark mode logo"></amp-img>
</header>
```

2). Create a `[hidden]` [boolean expression](https://amp.dev/documentation/components/amp-bind/?format=email#binding-types) comparing the value of `mode` with the current AMP state value. When the expression evaluates to `false`, the logo will no longer stay hidden and will become visible
```html
<header>
  <amp-img [hidden]="mode != 'lightMode'" src="https://YOUR-LIGHT-MODE-LOGO.png" height="48" width="40" layout="fixed" alt="Light mode logo"></amp-img>
  <amp-img [hidden]="mode != 'darkMode'" src="https://YOUR-DARK-MODE-LOGO.png" height="48" width="40" layout="fixed" alt="Dark mode logo"></amp-img>
</header>
```

3). Add a separate `hidden` attribute to the dark mode logo to prevent both logos from displaying upon initial email load
```html
<header>
  <amp-img [hidden]="mode != 'lightMode'" src="https://YOUR-LIGHT-MODE-LOGO.png" height="48" width="40" layout="fixed" alt="Light mode logo"></amp-img>
  <amp-img hidden [hidden]="mode != 'darkMode'" src="https://YOUR-DARK-MODE-LOGO.png" height="48" width="40" layout="fixed" alt="Dark mode logo"></amp-img>
</header>
```

Now when the user selects dark mode, the appropriate logo will display:

![Replacing logos in dark mode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4yy9r76fsakavur6cac8.gif)

---

## 🌃 Conclusion
Through the use of `amp-bind` and the `AMP.setState()` action, users can select and apply a variety of color schemes to the AMP email template. Also, by leveraging `[hidden]` expressions, images such as logos can be replaced with dark mode versions.

> Although there is no support for the media feature `prefers-color-scheme`, **AMP email tools are available to help create customized color schemes experiences that can benefit email users**.