# Email Design

> Design distinctive, on-brand emails — newsletters, campaigns, welcome series, promo, post-purchase, win-back, transactional, announcements. Use when the user wants to draft, write, compose, design, redesign, critique, polish, or improve an email; or send a campaign / blast / broadcast through any ESP (Nitrosend, Klaviyo, Mailchimp, ActiveCampaign, etc.). Produces a structured design brief and translates it into the sending tool's section format. Bakes in anti-slop principles: own a colour, restrained palettes, real imagery with sourced or prompted image slots, distinctive voice, no category-reflex aesthetics, no orphan whitespace, single column ≤600px, mobile-first, real footers (verified mission only, plus socials + address + unsubscribe). Asks at most three questions to fill brand gaps; otherwise infers from connected ESP account settings or a project brand file. Self-contained — no other skills required. Skip for pure strategy, deliverability, copy-only, or platform-selection questions.

- Skill: `cosmoblk/email-design` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add cosmoblk/email-design`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cosmoblk/email-design/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- License: MIT
- Author: cosmoblk (https://skillmd.com/u/cosmoblk)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cosmoblk/email-design

---


# Email Design Skill

Distinctive, on-brand emails. Produces a structured brief, then hands it to the connected sending tool. Self-contained.

## When to use

Triggered for:

- "draft / write / compose / design / build an email" (newsletter, campaign, blast, broadcast, announcement, welcome, win-back, post-purchase, abandoned cart, promo, transactional)
- "make this email less generic / less plain / more on brand"
- "redesign / critique / polish this email"
- "send a campaign through Nitrosend / Klaviyo / etc."

Skip for:

- Strategy-only ("what frequency should I send at?")
- Deliverability-only ("why am I in spam?")
- Platform selection ("Klaviyo vs Mailchimp?")
- Copywriting in isolation, no design

## The flow (five steps)

1. **Context check** — pull brand state from the connected ESP and project files. No questions yet.
2. **Ask up to three questions** — only the highest-leverage gaps. Bundle into one message.
3. **Pick an archetype, write the brief.** Commit before composing.
4. **Resolve image slots** — fetch from brand site, brand assets, or prompt the user.
5. **Translate the brief into the sending tool's format** and run a dry-run preview before approval.

---

## Step 1 — Context check (silent)

Pull what is already known. **Do not ask the user yet.**

If Nitrosend MCP tools are available (`nitro_*`):

- Read the `nitro://account` resource for brand fields (company name, brand colour, logo, fonts, physical address, voice document if present).
- Call `nitro_get_status` for onboarding state and any obvious gaps.
- Call `nitro_query({ entity: "templates", limit: 3 })` to see prior design language. Look at the actual sections, colours, typography, and tone.

If the connected ESP is something else, look for the equivalent — most ESPs expose a brand or settings endpoint. If the ESP is unknown, fall back to project files: `BRAND.md`, `email-brand.md`, `PRODUCT.md`, or a top-level `README.md` for tone clues.

Build a brand profile in your head with these fields, marking each as `known`, `inferable`, or `missing`:

- Company name, from-name, from-email, mission/tagline
- Primary brand colour (hex), secondary if any
- Logo URL
- Voice descriptors (e.g. founder-personal, editorial, cheeky, technical, premium-restrained)
- Footer essentials: physical address, social handles, unsubscribe handler

If three or more fields are `missing`, you can ask. Otherwise default to inferred values and note them in the brief.

---

## Step 2 — Up to three questions (only if needed)

Hard cap: **three questions, in one message, as a numbered list**. If more fields are missing, fill with sensible defaults and note them in the brief — do not interrogate.

Priority order — pick the top three that are missing and matter for *this specific email*:

1. **Email purpose** — what does this specific email need to do? (Drive a click? Tell a story? Confirm an action? Re-engage a lapsed user? Tease a launch?) Almost always worth asking on first email of a session.
2. **Tone register** — editorial, bold-mono, minimal-lux, founder-letter, punk/character, or lookbook? Ask only if voice is unclear from prior templates.
3. **Primary brand colour** — hex preferred. Ask only if missing AND no logo to extract from.
4. **Mission line** — one sentence on why the company exists (not what it sells). Ask only if footer-critical and absent.
5. **Socials + physical address** — handles + one mailing address for footer / CAN-SPAM. Ask only if footer would otherwise be incomplete.

If the user has already answered these earlier in the session, do not re-ask.

---

## Step 3 — Pick an archetype, then write the brief

Anti-slop wins. Commit to an archetype before drafting. The most common failure mode is defaulting to "minimal-lux" because it feels safe — it is the right answer maybe 30% of the time. For the rest, pick deliberately.

### Archetypes

| Archetype | When | Hallmarks | Reference brands |
|---|---|---|---|
| **Editorial** | Newsletters, brand stories, long-form | Serif heads, full-bleed photo, generous leading, single column, voice-rich | Patagonia, Tracksmith, Lucy Folk |
| **Bold mono** | Promo, launches, statements | One saturated colour drenches the surface, big punchy type, minimal copy | Absolut, Liquid Death, Fly By Jing |
| **Minimal lux** | Premium, transactional, restraint as positioning | Tinted neutrals, narrow width 472–520px, one accent, lots of breath, never discount-led | Stripe, Aesop, Apple, MoMA |
| **Founder letter** | Welcome, win-back, milestones, reply-driver | Plain-text feel, first-person, short paragraphs, signed off, no template chrome | Ugmonk, Superhuman, Tracksmith CEO |
| **Punk / character** | Brand voice carriers, retention | Heavy personality, mascot or character, irreverent copy, custom imagery | Frank Body, Duolingo, Liquor Loot, Chubbies |
| **Lookbook** | Product, fashion, food, paint, anything where the product *is* the design | Editorial photo > copy, gallery layout, full-bleed hero | Dior, Clare Paint, Starbucks seasonal |

### Shared visual laws

Apply to every archetype. Implementation should match the aesthetic — bold needs committed; minimal needs precision.

#### Colour

- Pick a strategy *before* picking colours:
  - **Restrained** — tinted neutrals + one accent ≤10% of the surface. Default for transactional and minimal-lux only.
  - **Committed** — one saturated colour carries 30–60% of the surface. Default for brand-identity emails.
  - **Drenched** — the surface IS the colour. For heroes and statement emails.
- Never pure `#000` or `#fff`. Tint every neutral toward the brand hue.
- High chroma at extreme lightness looks garish; pull saturation back as you approach black or white.
- **Category-reflex check.** If someone could guess the palette from the category alone — fintech → navy + gold, wellness → white + sage, AI → black + neon, ecommerce → millennial pink + cream — rework it. Training-data reflexes make emails forgettable.

#### Typography

- One typeface family per email. Add a second only with a clear functional reason.
- Body 14–16px. Headline 22–34px. Hierarchy ratio ≥1.25 between steps.
- Cap line length 50–65 characters.
- No gradient text. No `background-clip: text`. Emphasis through weight or size, not effects.

#### Layout

- Single column. Width 472–600px. Wider feels desktopy and breaks on mobile.
- Vary spacing for rhythm. Same padding everywhere reads as wireframe.
- Cards are the lazy answer. Use only when truly the best affordance. Nested cards are always wrong.
- Touch targets ≥44×44px.

#### Whitespace (this is where most AI-drafted emails fail)

The default LLM behaviour is to over-pad — every section gets generous top *and* bottom padding, plus standalone spacer rows. The result is a moonscape. Counteract:

- **No orphan spacers.** Two `spacer` sections in a row, ever, is wrong. Collapse.
- **Asymmetric padding.** Most sections: `padding_top: 0, padding_bottom: 32`. Let the next section's top padding (or its lack) own the rhythm.
- **Hero hugs.** Hero image sits flush to header (≤8px gap), generous space *below* the hero, not both sides.
- **End rule.** Final content section before footer: `padding_bottom: 32–48px`, never more. Footer has its own top padding.
- **Never:** two consecutive empty paragraphs, double `<br>` between blocks, margin AND padding on the same side, an empty `text` section used as a spacer.

This applies regardless of platform whitespace-trimming passes — belt and braces.

#### Imagery

- Real photography or commissioned illustration only. No stock, no AI slop, no flat geometric shapes as decoration.
- Hero must serve a purpose: product, person, place, or moment. Decoration is not enough.
- Each image ≤200KB; total payload ≤800KB.
- Alt text on every image. Treat as fallback for the ~33% who block images.
- Dark mode: transparent PNGs for logos and icons; off-white (not pure white) backgrounds.

#### Copy

- Inverted pyramid. Lead with the answer.
- Short paragraphs. Every word earns its place.
- No restated headings. No intro paragraphs that repeat the subject.
- No em dashes (—) or en dashes (–) in body. Use commas, colons, semicolons, periods, parentheses. No `--`.
- One CTA above the fold. A second CTA at the bottom is fine. More than two is dilution.
- Subject ≤45 chars. Preheader ≤90 chars. Both must add information; never restate the subject.

### The brief (structured)

Write the brief as JSON before composing sections. This is what feeds the sending tool. Treat it as the contract.

```json
{
  "name": "Campaign or template name",
  "subject": "subject line ≤45 chars",
  "preheader": "preheader ≤90 chars adding new info",
  "archetype": "editorial | bold-mono | minimal-lux | founder-letter | punk-character | lookbook",
  "color_strategy": "restrained | committed | drenched",
  "primary_color": "#hex",
  "secondary_color": "#hex or null",
  "voice_descriptors": ["e.g. founder-personal", "wry", "concrete"],
  "structure": [
    { "kind": "header",         "logo": true, "padding": "compact" },
    { "kind": "hero",           "image_slot": "<slot-id>" },
    { "kind": "headline",       "content": "..." },
    { "kind": "body",           "content": "..." },
    { "kind": "cta",            "label": "...", "url": "..." },
    { "kind": "secondary_body", "content": "..." },
    { "kind": "footer" }
  ],
  "image_slots": [
    {
      "slot_id": "hero",
      "purpose": "what it must convey",
      "candidate_url": "<url-from-brand-site or null>",
      "user_prompt": "<text to show user if no candidate>"
    }
  ],
  "footer": {
    "mission_line": "exact verified copy from brand fields or brand site, or null — never invent",
    "socials": [{ "name": "instagram", "url": "..." }],
    "physical_address": "one line",
    "unsubscribe": "{{unsubscribe_url}}"
  },
  "anti_slop_checks": ["no category-reflex palette", "real imagery", "distinctive voice"]
}
```

---

## Step 4 — Resolve image slots

Each `image_slot` must end with a real candidate URL or an explicit prompt to the user. Sources, in priority order:

1. **Already-uploaded brand assets.** If Nitrosend is connected: `nitro_query({ entity: "media" })`. Use what's there before fetching anything new.
2. **Brand site.** Fetch `https://<brand-domain>/` with WebFetch and look for:
   - `<meta property="og:image">`
   - hero image on the homepage (look for `<section class="hero"> img`, `<header> img`, `[data-hero] img`, banner-y filenames)
   - `/press`, `/about`, or `/blog` page imagery for portraits and lifestyle
   - Prefer images ≥1200px on the long edge.
3. **Brand social profiles.** Instagram or X profile images for portraits and lifestyle moments.
4. **Prompt the user — once.** Concrete description, options included: *"For the hero I'm picturing [X]. Got an image for that, or want me to leave a placeholder so you can drop one in via the media uploader?"*

Never fabricate or AI-generate a stock-style image. A clearly-marked placeholder is better than visual slop.

If you uploaded a new image and the brand uses Nitrosend: `nitro_ingest_image({ url: "...", purpose: "hero" })`.

---

## Step 5 — Translate the brief and hand off

Map the brief's `structure` array to the sending tool's section types. For Nitrosend `nitro_compose_campaign`:

| Brief kind | Nitrosend section | Notes |
|---|---|---|
| header | omit if logo lives in `theme.logo_url`; otherwise `image` | Logo height 32–48px |
| hero | `image` | Full-width, no side padding, set `link_url` only if it's clickable |
| headline | `text` with `<h1>` or `<h2>` markup | Don't add a separate spacer above; let the next section's top padding handle rhythm |
| body | `text` | Short paragraphs |
| cta | `button` | Single CTA, full-width on mobile |
| secondary_body | `text` | Optional |
| divider | `divider` | Once per email max |
| footer | `footer` | Auto-rendered if account footer is configured; otherwise build explicitly |

### Whitespace overrides on the call

Override the platform's default padding when composing. Do this even if the platform has a whitespace-trimming pass.

```json
{
  "type": "image",
  "props": {
    "src": "...",
    "alt": "...",
    "padding_top": 0,
    "padding_bottom": 24
  }
}
```

Default rule per section: `padding_top: 0, padding_bottom: 32`. Override only with reason.

### Footer — the part most AI emails get wrong

A great footer, in this order:

1. **Mission line.** One sentence. *Why* the company exists, not what it sells. ("Made for runners. Made in Boston." beats "Premium athletic apparel since 2014.") **Do not guess or invent this.** Pull verbatim from one of: the connected ESP's brand fields (Nitrosend: `company_description` on the brand, also visible in the Brand settings), or the brand's homepage / about page (hero subhead, footer tagline, "we believe…" copy). If neither yields an exact, verified line, **omit the mission line entirely** — a missing mission is better than a fabricated one.
2. **Social icons.** 3–5 max. Real handles, real URLs. 24–32px icons.
3. **Physical address.** Required for CAN-SPAM. Single line.
4. **Unsubscribe link.** Visible, not microscopic. The word "unsubscribe", not a buried "preferences" euphemism. Required for CAN-SPAM and Gmail/Yahoo bulk-sender rules.
5. **Optional:** tagline, contact email, "you're getting this because…" reason.

Visual: smaller type (12–13px), tinted neutral text at 60–70% of body contrast, generous internal padding, no extra spacer above.

### The handoff

Always run a dry-run first and always show the preview URL before the user approves a real send. Ask if they want a test email.

```
nitro_compose_campaign({
  name: brief.name,
  subject: brief.subject,
  preheader: brief.preheader,
  sections: <translated from brief.structure with explicit padding>,
  theme: {
    brand_color: brief.primary_color,
    bg_color: ...,
    text_color: ...,
    font_body: ...,
    font_heading: ...,
    logo_url: ...
  },
  audience: { audience_type: "lists", contact_list_ids: [...] },
  dry_run: true
})
```

Then surface the preview URL from the response. Only proceed to a real send after the user confirms.

### Offer a test send

After the dry-run preview, before any real send, ask:

> *"Want me to send you a test email first? I can send to [from_email from account] or another address — let me know."*

Default the recipient to the account's `from_email` or the saved test recipients, but accept any address the user names. If they say yes, send via the platform's test endpoint and surface the result.

For Nitrosend:

```
nitro_send_test_message({
  template_id: <id from compose response>,
  email: "<recipient>"
})
```

For other ESPs, use the equivalent (`POST /v1/my/templates/{id}/send_test` on the Nitrosend REST API; analogous endpoints elsewhere). After the test lands, ask if any final tweaks are needed before the real send.

---

## Pre-handoff checklist

Run this mentally before composing. If three or more fail, redraft.

- [ ] Subject ≤45 chars, adds information
- [ ] Preheader ≤90 chars, doesn't restate subject
- [ ] One archetype, applied consistently
- [ ] Colour strategy committed (not "neutrals + accent" by reflex unless minimal-lux)
- [ ] No category-reflex palette
- [ ] At least one real image, sourced or explicitly prompted
- [ ] Single CTA above the fold; ≤2 total
- [ ] Footer has socials + physical address + unsubscribe (mission line only if verified — never invented)
- [ ] No double spacers, no `<br><br>`, no margin+padding combos
- [ ] No em dashes in body
- [ ] Single column ≤600px; touch targets ≥44px
- [ ] Voice is recognisable as the brand, not a template
- [ ] Offered a test send to the user before any real send

## Pushback patterns

When the user wants to iterate:

- **"Make it bolder"** → escalate colour strategy (restrained → committed → drenched), increase type contrast, shrink line lengths, swap to bold-mono or punk-character archetype.
- **"Make it quieter"** → de-escalate colour, increase whitespace selectively (not uniformly — that's the moonscape failure mode), strip secondary CTAs, swap to minimal-lux or editorial.
- **"Add personality"** → swap to founder-letter or punk-character; rewrite copy first-person; add one unexpected element (handwritten asset, mascot, ridiculous subject line).
- **"Too plain"** → almost always means the archetype is wrong. If you defaulted to minimal-lux, ask once whether they want bold-mono or lookbook instead.
- **"Looks like AI made it"** → category-reflex palette, stock-feeling imagery, or no voice. Diagnose which one and fix only that.

## Examples

### Brief — SaaS welcome email (founder-letter)

```json
{
  "name": "Welcome 1 — Founder Hello",
  "subject": "you're in",
  "preheader": "a quick note from the founder, plus what to expect this week",
  "archetype": "founder-letter",
  "color_strategy": "restrained",
  "primary_color": "#1f2933",
  "voice_descriptors": ["first-person", "concrete", "warm", "no marketing fluff"],
  "structure": [
    { "kind": "header", "logo": true, "padding": "compact" },
    { "kind": "headline", "content": "Hey, I'm [name]." },
    { "kind": "body", "content": "Three short paragraphs. What we built, why we built it, what to do this week." },
    { "kind": "cta", "label": "Reply with one thing you're trying to do", "url": "mailto:..." },
    { "kind": "footer" }
  ],
  "image_slots": [],
  "footer": {
    "mission_line": "Building [thing] so [who] can [outcome].",
    "socials": [{ "name": "linkedin", "url": "..." }],
    "physical_address": "...",
    "unsubscribe": "{{unsubscribe_url}}"
  },
  "anti_slop_checks": ["plain-text feel keeps it personal", "no stock imagery", "single CTA is a reply, not a click"]
}
```

### Brief — D2C product launch (bold-mono, drenched)

```json
{
  "name": "Launch — Nitro Blue Drop",
  "subject": "blue.",
  "preheader": "the new colorway. limited run. tomorrow 9am.",
  "archetype": "bold-mono",
  "color_strategy": "drenched",
  "primary_color": "#1d4ed8",
  "voice_descriptors": ["punchy", "confident", "few words"],
  "structure": [
    { "kind": "hero", "image_slot": "hero" },
    { "kind": "headline", "content": "Blue." },
    { "kind": "body", "content": "Limited to 200. 9am tomorrow." },
    { "kind": "cta", "label": "Set a reminder", "url": "..." },
    { "kind": "footer" }
  ],
  "image_slots": [
    {
      "slot_id": "hero",
      "purpose": "Single product shot on solid background. Same blue as primary_color. No text overlay.",
      "candidate_url": "https://brand.com/og-image.jpg",
      "user_prompt": "Got the product photo of the blue colorway? Drop the URL or upload via the media uploader."
    }
  ],
  "footer": {
    "mission_line": "Made for runners. Made in [city].",
    "socials": [
      { "name": "instagram", "url": "..." },
      { "name": "tiktok", "url": "..." }
    ],
    "physical_address": "...",
    "unsubscribe": "{{unsubscribe_url}}"
  },
  "anti_slop_checks": ["committed to one colour", "real product photo, not stock", "no double-padding"]
}
```

### Brief — Editorial newsletter (Patagonia-style)

```json
{
  "name": "Newsletter — Running gear for winter's worst",
  "subject": "running gear for winter's worst",
  "preheader": "what we wear when it's brutal out there",
  "archetype": "editorial",
  "color_strategy": "committed",
  "primary_color": "#0a3d2e",
  "voice_descriptors": ["earnest", "specific", "outdoorsy", "sells the weather, not the product"],
  "structure": [
    { "kind": "header", "logo": true, "padding": "compact" },
    { "kind": "hero", "image_slot": "winter-trail" },
    { "kind": "headline", "content": "When the weather wins, you don't." },
    { "kind": "body", "content": "Short essay — 120 words on running in horizontal sleet. No product names yet." },
    { "kind": "cta", "label": "See the kit", "url": "..." },
    { "kind": "secondary_body", "content": "Three pieces, briefly. Photo above each, not in a grid card." },
    { "kind": "footer" }
  ],
  "image_slots": [
    {
      "slot_id": "winter-trail",
      "purpose": "Editorial landscape — runner small in frame, weather doing the talking. No product close-ups in the hero.",
      "candidate_url": null,
      "user_prompt": "Have a winter trail / weather shot? Or shall I leave a placeholder?"
    }
  ],
  "footer": {
    "mission_line": "We're in business to save our home planet.",
    "socials": [{ "name": "instagram", "url": "..." }],
    "physical_address": "...",
    "unsubscribe": "{{unsubscribe_url}}"
  },
  "anti_slop_checks": ["no category-reflex green-and-brown palette", "hero sells the moment, not the product", "voice carries the email, not the design chrome"]
}
```

