_emailmd_

Theme

Customize colors and fonts to match your brand.

Customize colors and fonts to match your brand:

const { html } = await render(markdown, {
  theme: {
    brandColor: '#e11d48',
    buttonColor: '#e11d48',
    fontFamily: 'Georgia, serif',
  }
});

Dark Theme

A built-in dark theme is available. Pass it directly to render the email dark for everyone:

import { render, darkTheme } from 'emailmd';

const { html } = await render(markdown, { theme: darkTheme });

Or use it as a base and override individual values:

const { html } = await render(markdown, {
  theme: { ...darkTheme, brandColor: '#e11d48' },
});

You can also activate it per-email via frontmatter; see Frontmatter.

Automatic Dark Mode

To render light by default but adapt to the reader's dark-mode preference, opt in with darkTheme:

// Built-in dark palette
const { html } = await render(markdown, { darkTheme: true });

// Or override parts of it
const { html } = await render(markdown, {
  darkTheme: { brandColor: '#22d3ee' },
});

Or per-email in frontmatter with theme: auto:

---
theme: auto
---
---
theme: auto
dark:
  background_color: "#111827"
  content_color: "#1f2937"
---

The dark: map tunes the dark variant (and implies auto on its own). Explicitly pinning theme: light or theme: dark renders static even when the rendering app passes darkTheme: the author's pinned appearance wins.

This emits color-scheme meta tags, a prefers-color-scheme: dark media query, and [data-ogsc]/[data-ogsb] selectors for Outlook.com. Client support varies: Apple Mail and Outlook honor these overrides; Gmail ignores them and applies its own automatic color inversion regardless. Buttons keep their configured colors in both modes, so pick button colors that work on light and dark backgrounds.

Dark mode is palette-based: custom per-block colors (bg=/color= on callouts and columns, divider colors) are normalized to the dark palette so text stays readable. To tune how a specific accent looks in dark mode, adjust the corresponding key in the dark: map rather than the block parameter.

The overrides target the stable emd-* classes below, which you can style yourself or reach from a custom wrapper.

CSS Hooks

Every render emits the same emd-* classes, so the css render option can reach what the theme keys don't:

ClassOn
emd-rootBody
emd-sEvery content section
emd-bgSections painted with contentColor
emd-top / emd-botFirst and last section of the content box
emd-heroHero sections
emd-cardCallout / column card backgrounds
emd-hlHighlight blocks
emd-tblTables
emd-btnButtons

Rounding the Content Box

The content area is a stack of same-colored sections rather than one element, so emd-top and emd-bot mark its outer edges. They carry no styling of their own:

const { html } = await render(markdown, {
  css: `
    .emd-top, .emd-top > table { border-top-left-radius: 12px; border-top-right-radius: 12px; }
    .emd-bot, .emd-bot > table { border-bottom-left-radius: 12px; border-bottom-right-radius: 12px; }
  `,
});

Use the corner longhands rather than the border-radius shorthand — a short email is a single section carrying both classes, and two competing shorthands would leave one edge square — and style the section together with its child table, since MJML paints the background on both.

::: header and ::: footer bands sit on the outer background, outside the box; a hero at the top of the document counts as the top of it. Outlook desktop renders square corners.

Web Fonts

To use a font beyond the web-safe stack (e.g. Google Fonts), pass a fonts map alongside theme. Each entry renders as an <mj-font> tag, loading the stylesheet in clients that support embedded web fonts:

const { html } = await render(markdown, {
  fonts: {
    Inter: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap',
  },
  theme: {
    fontFamily: 'Inter, sans-serif',
  },
});

The font family still has to be named in fontFamily to actually apply; fonts only ships the stylesheet link. MJML drops unused entries automatically.

The same thing works from markdown with a nested fonts: map in frontmatter. See Frontmatter → Custom Fonts.

All Theme Options

KeyLight DefaultDark DefaultDescription
brandColor#18181b#fafafaLinks, highlights, accents
headingColor#09090b#fafafaHeading text
bodyColor#71717a#a1a1aaBody text
backgroundColor#fafafa#09090bOuter background
contentColor#ffffff#18181bContent area background
cardColor#f4f4f5#27272aCallout / code block background
buttonColor#18181b#fafafaPrimary button background
buttonTextColor#fafafa#18181bButton text
secondaryColor#18181b#fafafaSecondary button border
secondaryTextColor#18181b#fafafaSecondary button text
successColor#16a34a(same)Success button background
successTextColor#ffffff(same)Success button text
dangerColor#dc2626(same)Danger button background
dangerTextColor#ffffff(same)Danger button text
warningColor#d97706(same)Warning button background
warningTextColor#ffffff(same)Warning button text
dividerColor#f4f4f5#27272aHorizontal rule (---) color
fontFamilyInter, ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif(same)Font stack
fontSize16px(same)Base font size
lineHeight1.6(same)Base line height
contentWidth600px(same)Email width
borderRadius8px(same)Border radius for buttons, callouts, highlights

The dark theme inverts the color palette (light text on dark backgrounds) while keeping the same typography settings. You can import darkTheme directly or activate it per-email via frontmatter.

On this page