email-designer

Generate Outlook-compatible email templates through conversation. The user describes what they want, you produce a pixel-perfect EML file that opens as a draft in Outlook.

aiden0z/skills72 installsMITSynced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI

Agent Skills format with YAML frontmatter. Claude Code reads it as-is.

---
name: "email-designer"
description: "Generate Outlook-compatible email templates through conversation. The user describes what they want, you produce a pixel-perfect EML file that opens as a draft in Outlook."
license: "MIT"
---

# Email Designer

Generate Outlook-compatible email templates through conversation. The user describes
what they want, you produce a pixel-perfect EML file that opens as a draft in Outlook.

## How It Works

This skill has three layers:
1. **Rules** — Outlook compatibility constraints you follow when generating HTML
2. **Templates** — Pre-built components and layouts you assemble from
3. **Code blocks** — Python scripts you execute to produce EML files

The core (HTML + EML) uses Python stdlib only. Optional features (charts, header
banners, image optimization) require additional packages that are auto-installed
when needed — see Step 0.

## Step 0: Environment Check

Before starting, verify that Python 3 is available. Try these commands (use whichever works on the current platform):

```sh
python3 --version    # macOS / Linux
python --version     # Windows (check that output shows 3.x, not 2.x)
```

- **Exists and Python ≥ 3.8** → continue silently (do not mention the check to the user).
- **Missing or version too low** → do NOT proceed. Instead:
  1. Run `uname -s` and `uname -m` (or `systeminfo` on Windows) to detect the user's OS and architecture.
  2. If you have web search capability, search for the latest recommended way to install Python 3 on the user's platform, and provide the specific commands.
  3. If you cannot search, show this message in the user's language:
     > **中文**: Email Designer 需要 Python 3.8 或更高版本。请访问 https://www.python.org/downloads/ 下载并安装适合你操作系统的版本,安装完成后重新运行。
     >
     > **English**: Email Designer requires Python 3.8 or later. Please visit https://www.python.org/downloads/ to download and install the version for your OS, then try again.
  4. Stop here and wait for the user to install before continuing.

### Optional Dependencies (auto-installed when needed)

If Step 1 determines the email needs charts or image processing:

1. Execute `code-blocks/deps-checker.py` → `check_and_install(features)`
   - `features` is `['charts']`, `['images']`, or `['charts', 'images']`
2. **All available** → continue silently
3. **Just installed** → continue silently (installation is quiet)
4. **Install failed** → inform user and offer alternatives:
   - Charts unavailable → "I'll use HTML tables and stats-grid components instead"
   - Images unavailable → "I'll use the HTML header component and skip compression"

| Feature | Packages | What It Enables |
|---------|----------|-----------------|
| `charts` | plotly, kaleido | Bar, line, heatmap, pie chart generation |
| `images` | pillow | Header banner compositing, image compression |
| `excel` | openpyxl | Excel template generation and data loading (Production Mode) |

### Mode Detection

After environment checks pass, determine the appropriate mode based on user input
and existing projects. The three modes are:

**1. Import Mode** — if the user provides a `.eml` file (or mentions importing/复刻 an
existing email):
1. Execute `code-blocks/eml-to-html.py` → `extract_from_eml(eml_path)` to extract
   HTML and embedded images
2. Execute `save_extracted(result, output_dir)` to save HTML + images with CID
   references converted to relative paths
3. Auto-open the extracted HTML in browser for preview
4. Ask the user: "已提取邮件内容并预览。接下来您想:
   A) 在此基础上调整设计(进入设计模式 Step 3)
   B) 直接沉淀为可复用量产项目(进入沉淀流程)"
5. If A → validate the extracted HTML (`code-blocks/html-validator.py` → `validate(html)`),
   organize output via `code-blocks/output-manager.py` → `create_project(name)`,
   then skip to Step 3 (Preview & Adjust) with the extracted HTML as starting point
6. If B → read `rules/production-mode.md` § "Crystallization Process" and follow steps C0-C5

**2. Production Mode** — if `email-projects/` directory exists with crystallized projects:
1. Scan for subdirectories containing both `template.html` and `template.xlsx`
2. If projects exist, present them:
   > "检测到以下邮件模板项目:
   > 1. {project-name-1}
   > 2. {project-name-2}
   > ...
   > 请选择项目编号并提供 Excel 数据文件路径(如:1 /path/to/data.xlsx),
   > 或输入 'new' 创建新邮件。"
3. If user selects a project → first run `code-blocks/deps-checker.py` →
   `check_and_install(features=['excel'])` to ensure openpyxl is available,
   then read `rules/production-mode.md` and follow steps P0-P4
4. If user types `new` → continue with Design Mode

**3. Design Mode** — default when no EML file provided and no existing projects (or user
chose `new`). Continue with Step 1 below.

## Adaptive Flow

Not every request needs the full wizard. Match your approach to the user's input:

**If the user's request already contains enough info** (layout, colors, width, content
type are clear from context), skip ahead. For example, "帮我做一个蓝色的产品更新邮件
模板,600px" already tells you: single-column, blue (#2563eb), 600px. Go straight to
generating.

**If the request needs clarification**, use the guided interaction below to gather
what you need. The goal is to make decisions easy — especially for non-technical
users who may not know email design terminology.

### Guided Interaction

Email design has 6 decision dimensions. The agent's job is to infer what it can
from the user's request, then ask about the rest — one question at a time, always
with a recommended default.

**The 6 dimensions (ask in this order — earlier answers inform later defaults):**

| # | Dimension | What to decide | How to handle |
|---|-----------|---------------|---------------|
| 1 | **Purpose & Layout** | Email type → determines structure | Usually inferable from request. Confirm, don't ask from scratch |
| 2 | **Content Modules** | Which sections to include (KPI cards, charts, article list, table...) | Recommend a default set based on layout type. Let user add/remove |
| 3 | **Visual Style** | Color scheme, design tone | Ask unless user mentioned brand/color. Offer 3-4 named presets |
| 4 | **Data & Assets** | Has data files? Images? Logo? Need charts? | Inferable from layout type (dashboards need data). Ask to confirm |
| 5 | **Width** | 600 / 800 / 1200px | Default based on layout (newsletters→600, reports→800). Confirm |
| 6 | **Content** | Real content or placeholders? | Ask only if not already clear |

**Interaction principles:**

> **CRITICAL — DO NOT ask multiple questions in one message.**
> Each message MUST ask about ONE dimension only. Listing all questions
> at once overwhelms the user and defeats the guided interaction purpose.
>
> ❌ **BAD** (dumping all questions at once):
> "请告诉我:1. 报告内容范围 2. 视觉风格 3. 布局偏好 4. 邮件宽度 5. 其他"
>
> ✅ **GOOD** (one question per message, with recommendation):
> Message 1: "季度商机报告 — 我推荐 Dashboard 布局(800px)。通常包含:A) KPI 指标卡 B) 趋势图表 ..."
> Message 2: "配色风格?A) 深蓝商务 B) ..."
> Message 3: "你有现成的数据吗?A) 有 B) 先用示例数据"

- **Infer first, confirm second.** Parse everything the user already said. For
  "商机季度报告邮件", you already know: Dashboard layout, likely needs charts and
  KPI cards, 800px is appropriate. State your inference and ask for confirmation —
  don't present all 7 layouts and ask them to choose.

- **One question per message.** Each message asks about one dimension. This keeps
  the user focused and makes it easy to answer. If a dimension is already known
  (inferred or stated by user), skip it entirely.

- **Always recommend.** Every question includes a labeled recommendation with brief
  reasoning: "推荐 A(深蓝商务),适合正式的季度报告". Users who don't have a
  preference can just accept.

- **Multiple choice preferred.** Use A/B/C/D options with short descriptions rather
  than open-ended questions. Include an open option for customization (e.g., "D) 自定义颜色").

- **3 questions maximum.** If you've asked 3 questions and still have unknowns,
  use sensible defaults for the rest. Most users lose patience beyond 3 rounds.
  Combine questions only when two dimensions are tightly related (e.g., layout + width).

**Example flow for "帮我做一个商机季度报告邮件":**

Agent infers: Dashboard layout, 800px, needs charts/KPI.

Question 1 (confirm inference + ask modules):
> "季度商机报告 — 我推荐 Dashboard 布局(800px)。这类报告通常包含:
> A) KPI 指标卡  B) 趋势图表  C) 漏斗图  D) 分布图  E) Top 列表  F) 总结文字
> 推荐:全选,季度报告信息量大。你要哪些?"

Question 2 (visual style):
> "配色风格?
> A) 深蓝商务  B) 深绿增长  C) 紫色科技  D) 自定义颜色
> 推荐:A 深蓝商务,适合正式的季度报告"

Question 3 (data):
> "你有现成的数据吗?
> A) 有,我提供数据  B) 先用示例数据,之后替换"

Then start generating — no more questions.

### Step 1: Understand the Request

Using the guided interaction above, resolve these dimensions:

- **Layout**: Check `templates/layouts/` for options:
  - Single Column (单栏) — product updates, announcements, weekly digests
  - Two Column (双栏) — reports with sidebar data
  - Magazine (杂志) — editorial with hero image and articles
  - Announcement (公告) — minimal, centered, call-to-action
  - Dashboard (数据面板) — KPI reports, analytics digests, weekly reviews
  - Transactional (事务通知) — order confirmations, password resets, system alerts
  - Onboarding (引导流程) — welcome emails, getting-started guides
  - Custom — user describes, you design from components

- **Width**: 600px (mobile-first) / 800px (balanced) / 1200px (desktop) / custom

- **Colors**: Brand image provided? Analyze it for colors (read the image with vision).
  Otherwise, offer presets: Blue #2563eb, Green #059669, Orange #ea580c, Purple #7c3aed, Gray #374151.
  (Full palettes in `rules/brand-color-extraction.md`)

- **Saved templates**: Run `code-blocks/template-manager.py` → `list_templates()` to
  check for reusable templates. If any exist, offer them first.

### Material Assessment

After understanding layout/width/colors, assess what visual materials are needed:

- **User provided data** (Excel, CSV, table, numbers in conversation) → plan chart generation (Step 1.5a)
- **User provided background image** → plan header banner compositing (Step 1.5b)
- **User provided local images** → plan image optimization (Step 1.5c)
- **Email type is Dashboard/Weekly Report/Data Report but no data provided** → ask: "Do you have data you'd like visualized as charts?"

If any material preparation is needed:
1. Run dependency check (Step 0 optional deps)
2. Proceed to Step 1.5

If no materials needed → skip directly to Step 2.

### Step 1.5: Material Preparation (when materials identified)

Prepare visual assets before generating HTML. All outputs go to the project's `images/` directory.

#### 1.5a: Chart Generation (if data provided)

1. Read `rules/chart-design-system.md` for visual constraints
2. Analyze data structure → recommend chart type:
   - Categories × single value → horizontal bar
   - Categories × multiple series → stacked bar
   - Time × values → line chart
   - 2D matrix → heatmap
   - Parts of whole → pie/donut
   - ≤ 5 data points → suggest HTML table or stats-grid instead
3. Confirm with user: "Based on your data, I recommend a [type] chart. OK?"
4. Execute `code-blocks/chart-generator.py`:
   ```python
   gen = EmailChartGenerator(container_width=WIDTH, output_dir='OUTPUT/images')
   path = gen.bar_chart(categories=[...], series={...}, title='...', filename='chart_name.png')
   ```
5. Preview chart image with user, iterate if needed
6. Final PNG ready at `images/{chart_name}.png`

#### 1.5b: Header Banner (if background image provided)

1. Execute `code-blocks/header-generator.py`:
   ```python
   gen = HeaderGenerator()
   path = gen.generate(title='...', subtitle='...', output_path='OUTPUT/images/header_banner.jpg',
                       bg_image='path/to/bg.png')  # or bg_color='#2563eb' for solid
   ```
2. If no background image but user wants a visual banner → offer solid-color option
3. If user doesn't want a banner image → use the HTML header component instead (no Pillow needed)
4. Output: `images/header_banner.jpg`

#### 1.5c: Image Optimization (if local images provided)

1. Ensure images are in the project's `images/` directory
2. Check filenames — non-ASCII names must be renamed to ASCII (see `rules/design-system.md` § Image filename rules)
3. Execute `code-blocks/image-optimizer.py`:
   ```python
   results = optimize_directory('OUTPUT/images/', threshold_kb=200)
   # results = [(filename, original_kb, compressed_kb), ...]
   ```
4. Report savings to user if any files were optimized

### Step 2: Generate HTML

Before generating HTML, read these files for guidance:
- `rules/outlook-compatibility.md` — the Outlook compatibility rules (essential)
- `rules/design-system.md` — universal design foundation (colors, typography, spacing)
  (this is what makes emails look **professional and modern**, not just compatible)
- `rules/design-system-data-report.md` — ONLY for data-heavy emails (KPI dashboards,
  weekly reports, status updates). Skip this for simple newsletters/announcements.
- `rules/chart-design-system.md` — ONLY when generating charts. Color system,
  typography, sizing, and data label conventions for email-embedded charts.
- `rules/email-best-practices.md` — design guidelines
- `rules/style-presets.md` — design style presets (Corporate, Editorial, Minimal).
  Choose a style matching the email's purpose to guide spacing, font sizes, and color usage.
- `rules/placeholder-i18n.md` — use placeholders matching the user's language
- `templates/components/*.html` — proven Outlook-safe component patterns
  (includes stats-grid, nav-bar, status-badge for advanced layouts)
- `examples/example-*.html` — complete working examples for reference

**Design tip — keep headers compact**: The header should feel like a navigation bar,
not a hero banner. Logo + title on one line, total height around 60-70px. Avoid tall
spacer rows and oversized logos in the header — save vertical space for content.

**Why Outlook compatibility matters**: Outlook 2007-2019 on Windows uses Microsoft
Word's rendering engine instead of a browser engine. This means most modern CSS
(flexbox, grid, border-radius, margin) is ignored or broken. The rules file contains
battle-tested patterns from a production email system that handles this correctly.
Every HTML pattern in `templates/components/` has been verified to render correctly
across Outlook, Gmail, Apple Mail, and web clients.

Key principles when generating:
- **NEVER insert content the user did not request** — no "generated by AI" watermarks,
  no tool credits, no disclaimers. The footer should only include what the user's design
  calls for (copyright, unsubscribe link, etc.).
- **Follow `rules/outlook-compatibility.md` strictly** — it covers table layout, inline
  styles, VML dividers, spacer rows, MSO conditionals, and all other Outlook quirks.
  Read that file before generating any HTML.
- **Gmail size limit** — keep HTML under 102KB or Gmail clips the email.
- **Preheader text** — add a hidden `<div style="display:none;">Preview text</div>`
  right after `<body>`.

After generating:
1. **Validate first**: execute `code-blocks/html-validator.py` → `validate(html)`.
   Checks 32 rules including: forbidden CSS, missing VML, column width overflow, text overflow,
   Gmail 102KB size limit, missing alt text, non-HTTPS links, missing preheader,
   elements exceeding container width. Fix any errors before proceeding.
2. **Organize output**: execute `code-blocks/output-manager.py` → `create_project(name)`
   to create a timestamped directory in the **user's working directory**
   (e.g., `./output/2026-03-15_product-report/`). Never write output files
   into the skill's own installation directory.
3. Save HTML to `{project_dir}/newsletter-preview.html`

### Step 3: Preview & Adjust

1. Auto-open in browser: execute `code-blocks/preview-helper.py` → `open_in_browser()`
   - All images render directly (charts, header banner, content images)
   - No need to generate EML for preview
2. Show ASCII layout summary in conversation: → `ascii_layout_summary(html)`
3. Ask if adjustments needed, iterate until satisfied

### Step 4: Fill Content (Optional)

Ask: "Want to fill in content now, or leave placeholders for editing in Outlook?"

If filling now:
1. Execute `code-blocks/content-filler.py` → `generate_fill_template(html)` to show
   all placeholders as a YAML-like template the user can fill at once
2. Collect content — either one at a time OR as a batch dict from the user
3. Execute `fill_batch(html, content_dict, image_dir)` for efficient batch filling
   (auto-maps images from a directory to CID placeholders)
4. Re-preview and check `unfilled_placeholders(html)` for any remaining

### Quick Edits (anytime after Step 2)

If the user asks for a targeted change (e.g., "change the header color to red",
"make the title bigger"), use `code-blocks/html-patcher.py` instead of regenerating:
- `replace_color(html, old, new)` — swap a color everywhere
- `change_width(html, old_w, new_w)` — resize the container
- `replace_text(html, old, new)` — change visible text
- `add_section(html, section_html)` — insert before footer
- `patch_file(path, colors={...}, texts={...})` — apply multiple patches at once

Re-validate after patching: `html-validator.py` → `validate(patched_html)`.

### Step 5: Generate EML

This is where the HTML becomes a real email file. Two options:

**Option A (recommended): Use `eml-builder.py` fluent API**

Write a short Python script that uses the EMLBuilder class:

```python
from pathlib import Path
# Read the eml-builder.py source, then use:
eml = (
    EMLBuilder(sender="", subject="Newsletter Title")
    .set_html(Path("output/newsletter-preview.html").read_text())
    .add_image("logo", "output/images/logo.png")   # for each CID image
    .build("output/newsletter.eml")
)
```

**Option B: Use `html-to-eml.py` script template**

Copy the script, edit the CONFIG section at the top, then run it:

```python
HTML_FILE = "output/newsletter-preview.html"   # your generated HTML
OUTPUT_EML = "output/newsletter.eml"           # output path
SUBJECT = "..."                                # ask user
SENDER = ""                                    # optional
TO_ADDRS = []                                  # optional
IMAGE_DIR = "output/images"                    # if images exist
```

Both approaches use Python's built-in `email` module — no pip install needed. They create
a proper MIME structure (multipart/alternative → text/plain + multipart/related →
text/html + CID images) with `X-Unsent: 1` so Outlook opens it in draft/compose mode.

> **CRITICAL — Use the provided scripts only.**
> You MUST use `eml-builder.py` (Option A) or `html-to-eml.py` (Option B)
> to generate EML. DO NOT write your own temporary/inline Python script for
> EML generation — it will likely produce incorrect MIME structure or miss
> CID image embedding, resulting in broken images in Outlook.

**After EML is generated, validate before proceeding:**

1. Execute `code-blocks/eml-validator.py` → `validate_eml(eml_path)`.
   Checks 14 rules including: MIME structure, text/plain fallback, CID forward
   completeness (every `src="cid:xxx"` has a matching embedded image), residual
   unresolved image paths, Content-ID format (RFC 2392), X-Unsent header,
   Content-Disposition inline, and file size. Checks are adaptive — image-related
   rules only fire when the HTML actually references images; pure-text emails
   pass cleanly.
2. Fix any errors before proceeding to Step 6.

**After validation passes, you MUST proceed to Step 6** — do not end the conversation here.

### Step 6: Wrap Up

**Always perform all of the following**, in order:

#### 6a. Offer to Crystallize for Repeated Use

This is important — many users will want to reuse this template with different data
in the future (e.g., weekly reports, periodic newsletters). Always ask:

> "邮件设计完成。是否要将此模板沉淀为可复用项目?以后只需填 Excel 即可重复生成。"

If the user agrees, read `rules/production-mode.md` § "Crystallization Process" and follow steps C0-C5.
After crystallization completes, continue with 6b and 6c below.
If the user declines, continue with 6b and 6c below.

#### 6b. Save Template and Show Output

1. Offer to save the template: execute `code-blocks/template-manager.py` → `save_template()`
2. Show the output file locations using **absolute paths** (not just filenames) so the
   user can easily locate and open the files. For example:
   ```
   EML 文件:  /Users/name/project/output/2026-03-15_report/newsletter.eml
   HTML 预览: /Users/name/project/output/2026-03-15_report/newsletter-preview.html
   图片目录:  /Users/name/project/output/2026-03-15_report/images/
   ```
   Use the actual working directory path, not relative paths like `output/...`.

#### 6c. Show Usage Guide

Display the appropriate Outlook usage guide:
- Chinese user → `templates/guides/outlook-usage-guide-zh.md`
- English user → `templates/guides/outlook-usage-guide-en.md`

## Placeholder Systems

This skill uses two different placeholder mechanisms for different purposes:

- **`{{placeholder}}`** (Design Mode) — used by `code-blocks/content-filler.py` for
  one-time placeholder filling during interactive design. Simple string replacement.
  Use this when building emails from scratch in Design Mode.

- **`<!-- SECTION/FIELD -->` comments** (Production Mode) — used in crystallized
  `template.html` files as structural markers that the AI agent reads to understand
  where to insert Excel data. These are NOT processed by content-filler.py.
  See `rules/production-mode.md` for details.

The two systems serve different workflows and do not interact. content-filler.py
is a Design Mode tool; Production Mode uses the AI agent as the rendering engine.

## File Map

```
rules/
  outlook-compatibility.md   ← Read before generating HTML (Outlook rendering rules)
  design-system.md           ← Universal: colors, typography, spacing (ALWAYS read)
  design-system-data-report.md ← Extension: KPI cards, status badges, trends (data emails only)
  email-best-practices.md    ← Design guidance (widths, colors, typography)
  style-presets.md           ← 3 design styles: Corporate, Editorial, Minimal
  placeholder-i18n.md        ← Localized placeholder text (zh/en/ja)
  chart-design-system.md     ← Chart colors, typography, sizing (read before chart generation)
  brand-color-extraction.md  ← Color extraction + preset palettes
  production-mode.md         ← Production mode: detection, workflow, crystallization, Excel integration

templates/
  components/*.html          ← 19 Outlook-safe HTML building blocks
                                (header, section, card, table, image-placeholder,
                                 divider, footer, stats-grid, nav-bar, status-badge,
                                 progress-bar, button, callout, testimonial,
                                 feature-list, pricing-table, team-member,
                                 alert, timeline)
  layouts/*.md               ← 7 preset layout descriptions
                                (single-column, two-column, magazine, announcement,
                                 dashboard, transactional, onboarding)
  guides/*.md                ← End-user Outlook usage guides (zh/en)

code-blocks/
  html-validator.py          ← Run AFTER generating HTML, BEFORE EML (auto-check 32 rules)
  eml-validator.py           ← Run AFTER generating EML, BEFORE Step 6 (auto-check 14 rules)
  html-patcher.py            ← Targeted edits (color, width, text) without regenerating
  output-manager.py          ← Timestamped project directories for organized output
  eml-builder.py             ← EML builder class (fluent API)
  cid-embedder.py            ← Image scanning + placeholder PNG creation
  html-to-eml.py             ← HTML→EML conversion (edit CONFIG section, then execute)
  eml-to-html.py             ← Extract HTML + images from .eml files (Import Mode)
  content-filler.py          ← {{placeholder}} replacement + batch filling
  template-manager.py        ← Save/load/list custom templates
  preview-helper.py          ← Browser auto-open + ASCII layout
  deps-checker.py            ← Auto-install optional dependencies (charts, images)
  chart-generator.py         ← Plotly chart generation (bar, line, heatmap, pie)
  header-generator.py        ← Header banner image compositing (text on background)
  image-optimizer.py         ← Image compression (PNG→JPEG, resize, optimize)
  excel-template-generator.py ← Generate & load Excel data templates (openpyxl)

examples/
  example-single-column.html ← Complete 600px single-column reference
  example-two-column.html    ← Complete 800px two-column reference
```

More General & Other skills

← All General & Other skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY