Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 4 additions & 15 deletions content/integrations/email/client-previews.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,14 @@ section: Integrations > Email
layout: integrations
---

<Callout
type="enterprise"
title="Enterprise plan feature."
style={{ alignItems: "center" }}
text={
<>
Email client previews are currently only available on our{" "}
<a href="https://knock.app/pricing" target="_blank" rel="noopener">
Enterprise plan
</a>
.
</>
}
/>

## Overview

Email client previews allow you to test how your email notifications will render across different email clients. This feature is embedded directly in the Knock template editor, using Litmus technology to generate accurate previews without requiring you to leave the editor or send test emails.

## Availability

Email client previews are available on all paid plans (Starter and above). On the free plan, the client previews button appears in the template editor but is disabled. Hovering over the disabled button displays a tooltip prompting you to upgrade to Starter.

## Features

- Preview emails in popular clients including Gmail, Outlook, and Apple Mail.
Expand Down
107 changes: 103 additions & 4 deletions content/integrations/email/layouts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,16 @@ Inject account- and environment-level variables into your layout with the `vars.

Branding properties set in account settings are available under `vars.branding.*`:

- `vars.branding.logo_url`
- `vars.branding.icon_url`
- `vars.branding.primary_color`
- `vars.branding.primary_color_contrast`
| Variable | Description |
| ------------------------------------------- | ---------------------------------------------------------------------- |
| `vars.branding.logo_url` | Your brand logo URL. |
| `vars.branding.icon_url` | Your brand icon URL. |
| `vars.branding.dark_logo_url` | Logo for dark mode (falls back to `logo_url`). |
| `vars.branding.dark_icon_url` | Icon for dark mode (falls back to `icon_url`). |
| `vars.branding.primary_color` | Primary brand color for buttons and accents. Defaults to `#000000`. |
| `vars.branding.primary_color_contrast` | Text color used on primary-colored backgrounds. Defaults to `#FFFFFF`. |
| `vars.branding.dark_primary_color` | Primary color for dark mode (falls back to `primary_color`). |
| `vars.branding.dark_primary_color_contrast` | Contrast color for dark mode (falls back to `primary_color_contrast`). |

With [per-tenant branding](/multi-tenancy/per-tenant-branding), Knock resolves these properties against the `tenant_id` on the workflow run, falling back to account-level branding if the tenant has none set.

Expand Down Expand Up @@ -223,6 +229,99 @@ The `<!--[if !mso]>` conditional prevents Outlook, which doesn't support `prefer
}
/>

### Branding-aware buttons

The Knock default email layout wires button styles to your branding colors so buttons match your brand out of the box. This applies to both the legacy `.button` class and the visual editor's `.block-button` components.

#### Legacy `.button` class

If your templates use the legacy `.button` class, the default layout styles it with your branding colors:

- **Light mode.** Background and border use `primary_color`; text uses `primary_color_contrast`.
- **Dark mode.** Background and border use `dark_primary_color`; text uses `dark_primary_color_contrast`.

#### Visual editor buttons (`.block-button`)

Buttons inserted from the visual editor use the `.block-button` class with solid or outline variants:

- **Solid buttons (`.block-button--solid`).**

- Light mode: background uses `primary_color`, text uses `primary_color_contrast`.
- Dark mode: background uses `dark_primary_color`, text uses `dark_primary_color_contrast`.

- **Outline buttons (`.block-button--outline`).**
- Light mode: transparent background with `primary_color` text and border.
- Dark mode: transparent background with `dark_primary_color` text and border.

In light mode, the layout styles do not use `!important`, so per-button colors set in the visual editor's style attributes take precedence. This means you can customize individual button colors without modifying the layout.

#### Why dark mode uses `!important` overrides

In dark mode, the layout applies `!important` to button styles. This is necessary because the visual editor renders button colors as static inline styles that do not change between light and dark mode. Without layout-level overrides, buttons would display their light-mode colors on a dark background, making text unreadable.

The tradeoff: in light mode, per-button color customizations from the visual editor are respected. In dark mode, the layout forces branding colors on all buttons to ensure readability. If you need per-button dark mode colors, you can customize the layout's dark mode CSS.

<Callout
emoji="🌠"
title="Note:"
text={
<>
If your default layout was created before April 21, 2026, it does not
include branding-aware button styles. Either create a new layout (which
starts from Knock's latest default) or add the button styles from the
snippet below to your existing layout.
</>
}
/>

Here's the CSS the default layout uses for branding-aware buttons:

```css title="Branding-aware button styles"
/* Legacy button class */
.button {
background-color: {{ vars.branding.primary_color | default: "#000000" }};
border-color: {{ vars.branding.primary_color | default: "#000000" }};
color: {{ vars.branding.primary_color_contrast | default: "#FFFFFF" }};
}

/* Visual editor solid buttons */
.block-button.block-button--solid {
background-color: {{ vars.branding.primary_color | default: "#000000" }};
border-color: {{ vars.branding.primary_color | default: "#000000" }};
color: {{ vars.branding.primary_color_contrast | default: "#FFFFFF" }};
}

/* Visual editor outline buttons */
.block-button.block-button--outline {
background-color: transparent;
border-color: {{ vars.branding.primary_color | default: "#000000" }};
color: {{ vars.branding.primary_color | default: "#000000" }};
}

@media (prefers-color-scheme: dark) {
/* Legacy button - dark mode */
.button {
background-color: {{ vars.branding.dark_primary_color | default: vars.branding.primary_color | default: "#000000" }} !important;
border-color: {{ vars.branding.dark_primary_color | default: vars.branding.primary_color | default: "#000000" }} !important;
color: {{ vars.branding.dark_primary_color_contrast | default: vars.branding.primary_color_contrast | default: "#FFFFFF" }} !important;
}

/* Visual editor solid buttons - dark mode */
.block-button.block-button--solid {
background-color: {{ vars.branding.dark_primary_color | default: vars.branding.primary_color | default: "#000000" }} !important;
border-color: {{ vars.branding.dark_primary_color | default: vars.branding.primary_color | default: "#000000" }} !important;
color: {{ vars.branding.dark_primary_color_contrast | default: vars.branding.primary_color_contrast | default: "#FFFFFF" }} !important;
}

/* Visual editor outline buttons - dark mode */
.block-button.block-button--outline {
background-color: transparent !important;
border-color: {{ vars.branding.dark_primary_color | default: vars.branding.primary_color | default: "#000000" }} !important;
color: {{ vars.branding.dark_primary_color | default: vars.branding.primary_color | default: "#000000" }} !important;
}
}
```

### Injecting workflow run scope into a layout at runtime

You can reference workflow-run-scoped variables in your layout with standard Liquid syntax, as long as they appear _after_ `{{content}}` is first rendered. (For variables needed _above_ `{{content}}`, see [pre-content variables](/integrations/email/layouts#defining-pre-content-variables) below.)
Expand Down
101 changes: 101 additions & 0 deletions content/integrations/email/mjml.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,107 @@ To include plain HTML within an MJML layout, wrap it in `<mjml-raw>` tags. MJML

[Learn more about email layouts](/integrations/email/layouts).

### Dark mode support in MJML layouts

The Knock default MJML layout supports dark mode out of the box via the `prefers-color-scheme` CSS media query. To add dark mode support to a custom MJML layout, include the color scheme meta tags and CSS overrides in your `<mj-head>`:

```mjml title="MJML layout with dark mode support"
<mjml>
<mj-head>
<mj-raw>
<meta name="color-scheme" content="light dark" />
<meta name="supported-color-schemes" content="light dark" />
</mj-raw>
<mj-style>
:root { color-scheme: light dark; supported-color-schemes: light dark; }
@media (prefers-color-scheme: dark) { .email-wrapper, .email-body {
background-color: #262626 !important; color: #ffffff !important; } }
</mj-style>
</mj-head>
<mj-body>
<!-- layout content -->
</mj-body>
</mjml>
```

<Callout
emoji="🌠"
title="Note:"
text={
<>
Not every email client supports <code>prefers-color-scheme</code>. Clients
that don't will fall back to your light mode styles, so treat light mode
as the default experience.
</>
}
/>

### Branding-aware buttons in MJML layouts

The Knock default MJML layout wires button styles to your [branding colors](/integrations/email/layouts#using-variables-and-brand-attributes-in-a-custom-layout) so buttons match your brand out of the box. This applies to both the legacy `.button` class and the visual editor's `.block-button` components.

#### Button behavior in light and dark mode

- **Solid buttons (`.block-button--solid`).** Background uses `primary_color`, text uses `primary_color_contrast`. In dark mode, these switch to `dark_primary_color` and `dark_primary_color_contrast`.

- **Outline buttons (`.block-button--outline`).** Transparent background with `primary_color` text and border. In dark mode, these switch to `dark_primary_color`.

In light mode, per-button colors set in the visual editor take precedence. In dark mode, the layout applies `!important` to override inline styles and ensure readability on dark backgrounds.

#### Why dark mode uses `!important` overrides

The visual editor renders button colors as static inline styles that do not change between light and dark mode. Without layout-level overrides, buttons would display their light-mode colors on a dark background, making text unreadable. The layout uses `!important` in dark mode to force branding colors on all buttons.

<Callout
emoji="🌠"
title="Note:"
text={
<>
If your MJML layout was created before April 21, 2026, it does not include
branding-aware button styles or dark mode support. Either create a new
layout (which starts from Knock's latest default) or add the styles to
your existing layout.
</>
}
/>

Here's the CSS to add branding-aware buttons to a custom MJML layout:

```mjml title="Branding-aware button styles for MJML"
<mj-style>
/* Legacy button class */ .button { background-color: {{
vars.branding.primary_color | default: "#000000" }}; border-color: {{
vars.branding.primary_color | default: "#000000" }}; color: {{
vars.branding.primary_color_contrast | default: "#FFFFFF" }}; } /* Visual
editor solid buttons */ .block-button.block-button--solid { background-color:
{{ vars.branding.primary_color | default: "#000000" }}; border-color: {{
vars.branding.primary_color | default: "#000000" }}; color: {{
vars.branding.primary_color_contrast | default: "#FFFFFF" }}; } /* Visual
editor outline buttons */ .block-button.block-button--outline {
background-color: transparent; border-color: {{ vars.branding.primary_color |
default: "#000000" }}; color: {{ vars.branding.primary_color | default:
"#000000" }}; } @media (prefers-color-scheme: dark) { .button {
background-color: {{ vars.branding.dark_primary_color | default:
vars.branding.primary_color | default: "#000000" }} !important; border-color:
{{ vars.branding.dark_primary_color | default: vars.branding.primary_color |
default: "#000000" }} !important; color: {{
vars.branding.dark_primary_color_contrast | default:
vars.branding.primary_color_contrast | default: "#FFFFFF" }} !important; }
.block-button.block-button--solid { background-color: {{
vars.branding.dark_primary_color | default: vars.branding.primary_color |
default: "#000000" }} !important; border-color: {{
vars.branding.dark_primary_color | default: vars.branding.primary_color |
default: "#000000" }} !important; color: {{
vars.branding.dark_primary_color_contrast | default:
vars.branding.primary_color_contrast | default: "#FFFFFF" }} !important; }
.block-button.block-button--outline { background-color: transparent
!important; border-color: {{ vars.branding.dark_primary_color | default:
vars.branding.primary_color | default: "#000000" }} !important; color: {{
vars.branding.dark_primary_color | default: vars.branding.primary_color |
default: "#000000" }} !important; } }
</mj-style>
```

## MJML templates

Email templates can use MJML in two ways:
Expand Down
Loading