Use this skill whenever the user creates, fixes, converts or reviews an HTML email (welcome, transactional, newsletter, DEM, ESP template) or asks to test it on Mailtrap or MailUp, even when they do not explicitly say "HTML", "template" or "deliverability": it produces table-based emails with minimal inline CSS, an aligned text/plain part, one-click List-Unsubscribe, and validates them with the included linter down to 0 errors, targeting SpamAssassin spam <= 0.1 and HTML Check with no warnings outside the baseline. Do not use it for copywriting without code, for configuring DNS/SPF/DKIM or for managing lists and bulk sends.
Produces HTML emails that are production ready on the first try: table layout with HTML attributes, inline CSS reduced to typography alone, a minimal <style>, an aligned text/plain part, one-click unsubscribe headers. The expected result on Mailtrap is spam ≤ 0.1 and an HTML Check with only the allowed warnings (§5). On MailUp Check-up: zero problems.
The complete normative rules with R-xxx IDs are in references/rules.md. The linter is scripts/lint_email.py and the reference template is templates/reference-welcome-dark.html. If those files are not available, the essential rules below are enough and are binding.
0. Included scripts
Script
Use
scripts/lint_email.py
Rule validator. python3 scripts/lint_email.py email.html --text email.txt --subject "Subject" [--production] [--transactional] [--json]. Exit 1 if any MUST is violated, 0 when clean.
scripts/build_payload.py
Generates the Mailtrap JSON payload with List-Unsubscribe and One-Click. --help for the options.
Screenshots at 600px and 375px, with and without <style> (requires Playwright; if it is missing, skip gate G2 and say so in the report).
templates/reference-welcome-dark.html
Compliant template to start from (0 MUST violated).
references/rules.md
Complete R-xxx rules. Read it when a warning is not in the §5 baseline, or when you need the rationale behind a rule.
1. Mandatory workflow
Run the phases in order. Do not deliver before G1–G2 are green. If you have access to Mailtrap, run G3–G6 as well.
Brief. Work out or ask (once, in a single batch) what is missing:
type of email, brand, language and light/dark theme;
a palette of 2–3 colors, content, CTA and URLs;
sending platform (MailUp, Mailtrap Sending, SES…) and the syntax of the dynamic fields;
legal details for the footer.
If the brief is incomplete do not stall: use explicit placeholders and list them in the report.
Content design. Before writing code, settle:
subject: 35–50 characters;
preheader: 40–100 characters, complementary to the subject;
a single primary CTA;
text/image ratio ≥ 80/20;
which blocks to use (header, hero, card, benefits, social, footer).
Code. Start from the skeleton in §3 (or from templates/). Apply every MUST rule in §4.
Text/plain. Write the text-only version with the same content and the same links in clear text.
G1 Lint. Run python3 scripts/lint_email.py email.html --text email.txt --subject "…" and bring the violated MUSTs to 0. If the script is not there, work through the §6 checklist by hand.
G2 Render. Take screenshots with Playwright/Chromium at 600px and 375px, then repeat them without <style> and with images blocked. No breakage allowed.
Payload and test send.
scripts/build_payload.py generates the JSON payload (List-Unsubscribe included);
the send starts with scripts/send_mailtrap_sandbox.{ps1,sh} or with the Mailtrap connector (send-sandbox-email);
on Windows use PowerShell: $env:VAR="…" and not the VAR=… cmd syntax.
Without Mailtrap (no account or connector): declare G3–G6 as not run in the report, do not estimate them, and in exchange work through the manual checklist in §6 on top of the linter. Deliver anyway.
G3–G6 Mailtrap verification. Use get-sandbox-message-html-analysis, get-sandbox-message-spam-score and the blacklist and text reports. Compare against the §5 baseline: every entry outside the baseline is a defect to fix and resend. Do not declare a removable warning a "false positive".
G7 MailUp (if that is the sending platform): Check-up with no errors on blacklisted links, tracking enabled, unsubscribe present, weight, dynamic fields with defaults, summary, analytics, custom tracking domain, spam and code.
Delivery.
.html, .txt and payload.json files;
a report with the outcome of the gates, remaining warnings with their rationale, placeholders still to be replaced and SHOULD exceptions with their rationale;
a version number (v1, v2…) in the test subject.
2. Key principle (the cause of the historical mistakes)
The Mailtrap checker uses caniemail.com data and reports every CSS property present, even when there is a fallback. bgcolor next to background-color does not remove the warning: the CSS property has to be deleted.
→ Layout with HTML attributes. Inline CSS for typography only. <style> only for rounded corners and media queries.
4. Essential MUST rules (IDs in references/rules.md)
Structure and weight
Tables only, role="presentation" cellpadding="0" cellspacing="0" border="0", a 600px container, a 16px outer gutter built with cells (R-004/005/006).
Forbidden: <h1>, <script>, <form>, <iframe>, <video>, <svg>, <link>, @import, the on* handlers, empty tags and superfluous comments (R-007/008/009/011).
HTML under 100 KB (target 40 KB). Images ≤ 50 KB, GIFs ≤ 100 KB with a complete first frame (R-050/052/604).
CSS
Inline, onlyfont-family, font-size, line-height, font-weight, font-style, letter-spacing, color, text-decoration, text-align and mso-* are allowed (R-100).
Forbidden inline: padding, margin, background, border, border-radius, width, height, max/min-*, display, opacity, overflow, visibility, outline, box-shadow, position, float, text-transform, table-layout (R-101). The only exception is the preheader (display:none; mso-hide:all).
Mandatory substitutions (R-200…210):
Instead of
Use
background
bgcolor
padding
empty cells with height/width
border
outer table with bgcolor + cellpadding="1"
CSS width/height
width/height attributes
body margins
marginwidth/marginheight
default color
text/link on <body>
gap under images
font-size:0; line-height:0 on the cell
background images
a solid color
A single <style>, with one line of border-radius and one line of @media (max-width:620px){…}, without only screen and. No reset, no preheader classes, no prefers-color-scheme or :root (R-300…303).
No comment containing the string <style. The email must stay readable even without the style block (R-304/305).
Antispam and deliverability
Marketing vs transactional. The unsubscribe rules (R-405List-Unsubscribe header, R-454 footer link) apply to commercial and bulk email. For a purely transactional one (order confirmation, password reset, shipping) they are not required: use --transactional in the linter, still keep an "Email preferences" link, and state it in the report. All the other rules apply to both.
HTML 100% ASCII: accented characters as numeric entities (è) (R-402).
Never use invisible filler in the preheader (͏, ‌, repeated ): it costs +2.4 SpamAssassin points (R-401).
Preheader without color. No text in a color equal or close to the background, separators included (R-400/403).
Text/plain aligned with the HTML. List-Unsubscribe and List-Unsubscribe-Post: List-Unsubscribe=One-Click headers (R-404/405).
Subject 35–50 characters (60 max): no ALL CAPS, no !!!, no trigger words or more than one emoji (R-406).
Spam target ≤ 0.1, blacklist 0, no shorteners or bare IPs. Text/images ≥ 80/20. Sender domain with SPF+DKIM+DMARC, a monitored Reply-To (R-407…412).
Links (MailUp Check-up)
Absolute, working HTTPS links. Tracking enabled with a custom tracking domain (R-450/451).
UTM utm_source, utm_medium=email, utm_campaign, without spaces (R-452).
Dynamic fields in the platform syntax (MailUp [field]) with a default value. In links the protocol must be explicit (https://[field]) (R-453).
Unsubscribe in the footer, two clicks at most (R-454). tel: without spaces, asset paths without spaces (R-457/458).
Design and accessibility
color-scheme = darkorlight, never light dark without a complete dark theme (R-500).
Calculated contrast ≥ 4.5:1 for every normal text, footer included (R-501).
Fonts: headings ≥ 22px, body ≥ 16px on mobile (.txtm class), legal ≥ 12px, CTA ≥ 15px. A palette of 2–3 colors (R-502/503).
aria-hidden on glyphs and separators. lang and <title> filled in. DOM order = visual order (R-504/505).
<img> with width, height, border="0", a short alt, HTTPS src from the brand CDN (no placeholders). The cell carries height and bgcolor. Never put essential text inside an image only (R-600…603).
CTA VML + HTML with identical dimensions, area ≥ 46×46px, a single primary CTA (R-700…703).
Columns in whole px (no 33.33%), no spacing asymmetry when they stack (R-800…802).
Footer: reason for receiving, legal entity, address, VAT number, contacts, Unsubscribe, Preferences, Privacy, copyright and social. In production, zero placeholders (R-900/901).
5. Mailtrap baseline (the only allowed warnings)
Rule
Reason
style
The style block is needed for responsive and corners
@media, @media max-width
Mobile media queries
width/display/height/font-size/line-height inside the media query
Stacked columns and mobile typography
border-radius (1 line)
On Outlook the corners stay square
display on the preheader
It is the only way to hide it
SpamAssassin in testing: MISSING_MID 0.1 (the production MTA adds the Message-ID) and HTML_MESSAGE 0.0. Any other entry has to be fixed.
Mailtrap Market Support ≥ 95% (for Mailtrap ≥ 90% is acceptable, below 85% it has to be redone).
6. Manual checklist (when the linter is not there)
No style="" in the body with properties outside the whitelist, preheader aside
Every background color is bgcolor, every spacing is a cell, every border is a table with cellpadding="1"
<style> = 1 radius line + 1 @media (max-width:620px) line
grep -P '[^\x00-\x7F]' over the HTML finds nothing
No ͏/‌ in the source
<body> with bgcolor, text, link, marginwidth, marginheight
Contrasts ≥ 4.5:1 calculated (script or tool), footer and separators included
Every <img> has width, height, border="0", alt, HTTPS src; the cell has height and bgcolor
CTA VML and HTML with the same width and height
Columns in whole px with the correct sum
Subject 35–50 characters, preheader 40–100
Text/plain aligned, List-Unsubscribe and One-Click in the payload
HTTPS links with UTM and unsubscribe present
No placeholders if it is going to production
7. Delivery report format
Version: vN | HTML weight: xx KB | Subject: "…" (nn chars) | Preheader: nn chars
G1 Lint: PASS (0 MUST, n SHOULD) G2 Render: PASS (600/375/no-style/img-off)
G3 HTML Check: xx% — warnings: [baseline only | list of extras + fix]
G4 Spam: 0.1 (MISSING_MID) G5 Blacklist: 0 G6 HTML/Text: aligned
G7 MailUp Check-up: [outcome | not run]
SHOULD exceptions with rationale: …
Placeholders to replace: …
Next step: …
Always flag the uncertainties explicitly, for example the syntax of the platform's unsubscribe placeholder that still has to be verified on the account.
1---2name: padosoft-email-html-builder3description: Use this skill whenever the user creates, fixes, converts or reviews an HTML email (welcome, transactional, newsletter, DEM, ESP template) or asks to test it on Mailtrap or MailUp, even when they do not explicitly say "HTML", "template" or "deliverability": it produces table-based emails with minimal inline CSS, an aligned text/plain part, one-click List-Unsubscribe, and validates them with the included linter down to 0 errors, targeting SpamAssassin spam <= 0.1 and HTML Check with no warnings outside the baseline. Do not use it for copywriting without code, for configuring DNS/SPF/DKIM or for managing lists and bulk sends.4license: MIT5---67# Email HTML Builder89Produces HTML emails that are **production ready on the first try**: table layout with HTML attributes, inline CSS reduced to typography alone, a minimal `<style>`, an aligned text/plain part, one-click unsubscribe headers. The expected result on Mailtrap is **spam ≤ 0.1** and an **HTML Check with only the allowed warnings** (§5). On MailUp Check-up: **zero problems**.1011The complete normative rules with `R-xxx` IDs are in `references/rules.md`. The linter is `scripts/lint_email.py` and the reference template is `templates/reference-welcome-dark.html`. **If those files are not available, the essential rules below are enough and are binding.**1213---1415## 0. Included scripts1617| Script | Use |18|---|---|19| `scripts/lint_email.py` | Rule validator. `python3 scripts/lint_email.py email.html --text email.txt --subject "Subject" [--production] [--transactional] [--json]`. Exit 1 if any MUST is violated, 0 when clean. |20| `scripts/build_payload.py` | Generates the Mailtrap JSON payload with `List-Unsubscribe` and One-Click. `--help` for the options. |21| `scripts/send_mailtrap_sandbox.sh` / `.ps1` | Sandbox send. Variables: `MAILTRAP_TOKEN`, `MAILTRAP_INBOX_ID`. |22| `scripts/screenshot_email.py` | Screenshots at 600px and 375px, with and without `<style>` (requires Playwright; if it is missing, skip gate G2 and say so in the report). |23| `templates/reference-welcome-dark.html` | Compliant template to start from (0 MUST violated). |24| `references/rules.md` | Complete `R-xxx` rules. Read it when a warning is not in the §5 baseline, or when you need the rationale behind a rule. |252627---2829## 1. Mandatory workflow3031Run the phases **in order**. Do not deliver before G1–G2 are green. If you have access to Mailtrap, run G3–G6 as well.32331. **Brief.** Work out or ask (once, in a single batch) what is missing:34 - type of email, brand, language and light/dark theme;35 - a palette of 2–3 colors, content, CTA and URLs;36 - sending platform (MailUp, Mailtrap Sending, SES…) and the syntax of the dynamic fields;37 - legal details for the footer.3839 If the brief is incomplete do not stall: use explicit placeholders and list them in the report.402. **Content design.** Before writing code, settle:41 - subject: 35–50 characters;42 - preheader: 40–100 characters, complementary to the subject;43 - a single primary CTA;44 - text/image ratio ≥ 80/20;45 - which blocks to use (header, hero, card, benefits, social, footer).463. **Code.** Start from the skeleton in §3 (or from `templates/`). Apply **every** MUST rule in §4.474. **Text/plain.** Write the text-only version with the same content and the same links in clear text.485. **G1 Lint.** Run `python3 scripts/lint_email.py email.html --text email.txt --subject "…"` and bring the violated MUSTs to **0**. If the script is not there, work through the §6 checklist by hand.496. **G2 Render.** Take screenshots with Playwright/Chromium at **600px** and **375px**, then repeat them without `<style>` and with images blocked. No breakage allowed.507. **Payload and test send.**51 - `scripts/build_payload.py` generates the JSON payload (List-Unsubscribe included);52 - the send starts with `scripts/send_mailtrap_sandbox.{ps1,sh}` or with the Mailtrap connector (`send-sandbox-email`);53 - on Windows use **PowerShell**: `$env:VAR="…"` and not the `VAR=… cmd` syntax.548. **Without Mailtrap** (no account or connector): declare G3–G6 as *not run* in the report, do not estimate them, and in exchange work through the manual checklist in §6 on top of the linter. Deliver anyway.559. **G3–G6 Mailtrap verification.** Use `get-sandbox-message-html-analysis`, `get-sandbox-message-spam-score` and the blacklist and text reports. Compare against the §5 baseline: every entry outside the baseline is a defect to fix and resend. **Do not declare a removable warning a "false positive".**5610. **G7 MailUp** (if that is the sending platform): Check-up with no errors on blacklisted links, tracking enabled, unsubscribe present, weight, dynamic fields with defaults, summary, analytics, custom tracking domain, spam and code.5711. **Delivery.**58 - `.html`, `.txt` and `payload.json` files;59 - a **report** with the outcome of the gates, remaining warnings with their rationale, placeholders still to be replaced and SHOULD exceptions with their rationale;60 - a version number (`v1`, `v2`…) in the test subject.6162---6364## 2. Key principle (the cause of the historical mistakes)6566The Mailtrap checker uses caniemail.com data and **reports every CSS property present, even when there is a fallback**. `bgcolor` next to `background-color` does **not** remove the warning: the CSS property has to be **deleted**.67→ **Layout with HTML attributes. Inline CSS for typography only. `<style>` only for rounded corners and media queries.**6869---7071## 3. Reference skeleton7273```html74<!DOCTYPE html>75<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">76<head>77<meta charset="utf-8">78<meta name="viewport" content="width=device-width, initial-scale=1">79<meta http-equiv="X-UA-Compatible" content="IE=edge">80<meta name="x-apple-disable-message-reformatting">81<meta name="format-detection" content="telephone=no, date=no, address=no, email=no, url=no">82<meta name="color-scheme" content="dark">83<meta name="supported-color-schemes" content="dark">84<title>Title</title>85<!--[if mso]><noscript><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml></noscript><![endif]-->86<style>87.r10{border-radius:10px}.r9{border-radius:9px}.r9t{border-radius:9px 9px 0 0}88@media (max-width:620px){.wrap{width:100%!important}.col{display:block!important;width:100%!important}.vgap{display:block!important;width:100%!important;height:16px!important}.gut{width:20px!important}.h1m{font-size:26px!important;line-height:32px!important}.txtm{font-size:16px!important;line-height:24px!important}.imgm{width:100%!important;height:auto!important}}89</style>90</head>91<body bgcolor="#04050a" text="#aab3c5" link="#2ff5d6" vlink="#2ff5d6" alink="#2ff5d6" marginwidth="0" marginheight="0" topmargin="0" leftmargin="0">92<!--[if !mso]><!--><div aria-hidden="true" style="display:none; mso-hide:all;">Preheader of 40-100 characters, no invisible filler.</div><!--<![endif]-->93<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#04050a">94 <tr><td height="32" style="font-size:1px; line-height:32px;"> </td></tr>95 <tr><td align="center" valign="top">96 <table role="presentation" width="632" class="wrap" align="center" cellpadding="0" cellspacing="0" border="0"><tr>97 <td width="16" style="font-size:1px; line-height:1px;"> </td>98 <td align="center" valign="top">99 <table role="presentation" class="wrap" width="600" align="center" cellpadding="0" cellspacing="0" border="0">100 <!-- blocks -->101 </table>102 </td>103 <td width="16" style="font-size:1px; line-height:1px;"> </td>104 </tr></table>105 </td></tr>106 <tr><td height="32" style="font-size:1px; line-height:32px;"> </td></tr>107</table>108</body>109</html>110```111112### Block patterns (copy them, do not reinvent them)113114**Vertical and horizontal spacing** (instead of padding):115```html116<tr><td height="24" style="font-size:1px; line-height:24px;"> </td></tr>117<td class="gut" width="24" style="font-size:1px; line-height:1px;"> </td>118```119120**Card with border and corners** (instead of border and background-color):121```html122<table role="presentation" class="r10" width="100%" cellpadding="1" cellspacing="0" border="0" bgcolor="#1c2333"><tr><td>123 <table role="presentation" class="r9" width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#0d1018">124 <tr><td colspan="3" height="24" style="font-size:1px; line-height:24px;"> </td></tr>125 <tr><td width="20" style="font-size:1px; line-height:1px;"> </td><td><!-- content --></td><td width="20" style="font-size:1px; line-height:1px;"> </td></tr>126 <tr><td colspan="3" height="24" style="font-size:1px; line-height:24px;"> </td></tr>127 </table>128</td></tr></table>129```130131**Accessible H1 heading** (no `<h1>`):132```html133<td align="center" class="h1m" role="heading" aria-level="1" style="font-family:Arial, Helvetica, sans-serif; font-size:30px; line-height:38px; font-weight:700; color:#f2f4f8;">Title</td>134```135136**Bulletproof CTA** (VML and HTML with **identical** dimensions):137```html138<!--[if mso]>139<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="https://…" style="height:48px; v-text-anchor:middle; width:220px;" arcsize="21%" fillcolor="#2ff5d6" stroke="f"><w:anchorlock/><center style="color:#04050a; font-family:Arial, Helvetica, sans-serif; font-size:15px; font-weight:bold;">Browse the products</center></v:roundrect>140<![endif]-->141<!--[if !mso]><!-->142<table role="presentation" class="r10" width="220" cellpadding="0" cellspacing="0" border="0" bgcolor="#2ff5d6"><tr>143<td align="center" height="48" style="font-family:Arial, Helvetica, sans-serif; font-size:15px; line-height:48px; font-weight:700;"><a href="https://…" target="_blank" style="color:#04050a; text-decoration:none;">Browse the products</a></td>144</tr></table>145<!--<![endif]-->146```147148**Image in a card** (the cell carries height and bgcolor, so it stays intact when images are blocked):149```html150<td align="center" valign="middle" height="120" class="r9t" bgcolor="#161b27" style="font-size:0; line-height:0;"><a href="https://…" target="_blank"><img src="https://cdn.example.com/img/x@2x.jpg" width="174" height="120" border="0" alt="Short description" class="imgm"></a></td>151```152153**Columns that stack on mobile** (whole px, sum = container):154```html155<tr><td class="col" width="176" valign="top">…</td><td class="vgap" width="12" style="font-size:1px; line-height:1px;"> </td><td class="col" width="176" valign="top">…</td><td class="vgap" width="12" style="font-size:1px; line-height:1px;"> </td><td class="col" width="176" valign="top">…</td></tr>156```157158**Divider:**159```html160<tr><td height="1" bgcolor="#1c2333" style="font-size:1px; line-height:1px;"> </td></tr>161```162163---164165## 4. Essential MUST rules (IDs in `references/rules.md`)166167**Structure and weight**168- Tables only, `role="presentation" cellpadding="0" cellspacing="0" border="0"`, a 600px container, a 16px outer gutter built with cells (R-004/005/006).169- Forbidden: `<h1>`, `<script>`, `<form>`, `<iframe>`, `<video>`, `<svg>`, `<link>`, `@import`, the `on*` handlers, empty tags and superfluous comments (R-007/008/009/011).170- HTML under 100 KB (target 40 KB). Images ≤ 50 KB, GIFs ≤ 100 KB with a complete first frame (R-050/052/604).171172**CSS**173- Inline, **only** `font-family`, `font-size`, `line-height`, `font-weight`, `font-style`, `letter-spacing`, `color`, `text-decoration`, `text-align` and `mso-*` are allowed (R-100).174- **Forbidden inline:** padding, margin, background, border, border-radius, width, height, max/min-*, display, opacity, overflow, visibility, outline, box-shadow, position, float, text-transform, table-layout (R-101). The only exception is the preheader (`display:none; mso-hide:all`).175- Mandatory substitutions (R-200…210):176177 | Instead of | Use |178 |---|---|179 | `background` | `bgcolor` |180 | padding | empty cells with `height`/`width` |181 | border | outer table with `bgcolor` + `cellpadding="1"` |182 | CSS width/height | `width`/`height` attributes |183 | body margins | `marginwidth`/`marginheight` |184 | default color | `text`/`link` on `<body>` |185 | gap under images | `font-size:0; line-height:0` on the cell |186 | background images | a solid color |187188- A single `<style>`, with **one line** of `border-radius` and **one line** of `@media (max-width:620px){…}`, without `only screen and`. No reset, no preheader classes, no `prefers-color-scheme` or `:root` (R-300…303).189- No comment containing the string `<style`. The email must stay readable even without the style block (R-304/305).190191**Antispam and deliverability**192- **Marketing vs transactional.** The unsubscribe rules (`R-405` `List-Unsubscribe` header, `R-454` footer link) apply to **commercial and bulk** email. For a purely transactional one (order confirmation, password reset, shipping) they are not required: use `--transactional` in the linter, still keep an "Email preferences" link, and state it in the report. All the other rules apply to both.193- HTML **100% ASCII**: accented characters as numeric entities (`è`) (R-402).194- **Never use invisible filler** in the preheader (`͏`, `‌`, repeated ` `): it costs +2.4 SpamAssassin points (R-401).195- Preheader without `color`. No text in a color equal or close to the background, separators included (R-400/403).196- Text/plain aligned with the HTML. `List-Unsubscribe` and `List-Unsubscribe-Post: List-Unsubscribe=One-Click` headers (R-404/405).197- Subject 35–50 characters (60 max): no ALL CAPS, no `!!!`, no trigger words or more than one emoji (R-406).198- Spam target ≤ 0.1, blacklist 0, no shorteners or bare IPs. Text/images ≥ 80/20. Sender domain with SPF+DKIM+DMARC, a monitored `Reply-To` (R-407…412).199200**Links (MailUp Check-up)**201- Absolute, working HTTPS links. Tracking enabled with a **custom tracking domain** (R-450/451).202- UTM `utm_source`, `utm_medium=email`, `utm_campaign`, without spaces (R-452).203- Dynamic fields in the platform syntax (MailUp `[field]`) with a default value. In links the protocol must be explicit (`https://[field]`) (R-453).204- Unsubscribe in the footer, two clicks at most (R-454). `tel:` without spaces, asset paths without spaces (R-457/458).205206**Design and accessibility**207- `color-scheme` = `dark` **or** `light`, never `light dark` without a complete dark theme (R-500).208- **Calculated** contrast ≥ 4.5:1 for every normal text, footer included (R-501).209- Fonts: headings ≥ 22px, body ≥ 16px on mobile (`.txtm` class), legal ≥ 12px, CTA ≥ 15px. A palette of 2–3 colors (R-502/503).210- `aria-hidden` on glyphs and separators. `lang` and `<title>` filled in. DOM order = visual order (R-504/505).211- `<img>` with `width`, `height`, `border="0"`, a short `alt`, HTTPS src from the brand CDN (no placeholders). The cell carries `height` and `bgcolor`. Never put essential text inside an image only (R-600…603).212- CTA VML + HTML with identical dimensions, area ≥ 46×46px, a single primary CTA (R-700…703).213- Columns in whole px (no `33.33%`), no spacing asymmetry when they stack (R-800…802).214215**Footer:** reason for receiving, legal entity, address, VAT number, contacts, Unsubscribe, Preferences, Privacy, copyright and social. In production, **zero placeholders** (R-900/901).216217---218219## 5. Mailtrap baseline (the only allowed warnings)220221| Rule | Reason |222|---|---|223| `style` | The style block is needed for responsive and corners |224| `@media`, `@media max-width` | Mobile media queries |225| `width`/`display`/`height`/`font-size`/`line-height` inside the media query | Stacked columns and mobile typography |226| `border-radius` (1 line) | On Outlook the corners stay square |227| `display` on the preheader | It is the only way to hide it |228229SpamAssassin in testing: `MISSING_MID` 0.1 (the production MTA adds the Message-ID) and `HTML_MESSAGE` 0.0. **Any other entry has to be fixed.**230231Mailtrap Market Support ≥ 95% (for Mailtrap ≥ 90% is acceptable, below 85% it has to be redone).232233---234235## 6. Manual checklist (when the linter is not there)236237- [ ] No `style=""` in the body with properties outside the whitelist, preheader aside238- [ ] Every background color is `bgcolor`, every spacing is a cell, every border is a table with `cellpadding="1"`239- [ ] `<style>` = 1 radius line + 1 `@media (max-width:620px)` line240- [ ] `grep -P '[^\x00-\x7F]'` over the HTML finds nothing241- [ ] No `͏`/`‌` in the source242- [ ] `<body>` with `bgcolor`, `text`, `link`, `marginwidth`, `marginheight`243- [ ] Contrasts ≥ 4.5:1 calculated (script or tool), footer and separators included244- [ ] Every `<img>` has width, height, border="0", alt, HTTPS src; the cell has height and bgcolor245- [ ] CTA VML and HTML with the same width and height246- [ ] Columns in whole px with the correct sum247- [ ] Subject 35–50 characters, preheader 40–100248- [ ] Text/plain aligned, List-Unsubscribe and One-Click in the payload249- [ ] HTTPS links with UTM and unsubscribe present250- [ ] No placeholders if it is going to production251252---253254## 7. Delivery report format255256```257Version: vN | HTML weight: xx KB | Subject: "…" (nn chars) | Preheader: nn chars258G1 Lint: PASS (0 MUST, n SHOULD) G2 Render: PASS (600/375/no-style/img-off)259G3 HTML Check: xx% — warnings: [baseline only | list of extras + fix]260G4 Spam: 0.1 (MISSING_MID) G5 Blacklist: 0 G6 HTML/Text: aligned261G7 MailUp Check-up: [outcome | not run]262SHOULD exceptions with rationale: …263Placeholders to replace: …264Next step: …265```266267Always flag the uncertainties explicitly, for example the syntax of the platform's unsubscribe placeholder that still has to be verified on the account.
Run npx skillmds@latest add padosoft/padosoft-email-html-builder in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Use this skill whenever the user creates, fixes, converts or reviews an HTML email (welcome, transactional, newsletter, DEM, ESP template) or asks to test it on Mailtrap or MailUp, even when they do not explicitly say "HTML", "template" or "deliverability": it produces table-based emails with minimal inline CSS, an aligned text/plain part, one-click List-Unsubscribe, and validates them with the included linter down to 0 errors, targeting SpamAssassin spam <= 0.1 and HTML Check with no warnings outside the baseline. Do not use it for copywriting without code, for configuring DNS/SPF/DKIM or for managing lists and bulk sends. It is listed under Web & Frontend on SkillMD.
This skill has not completed SkillMD's automated safety review yet. Capability flags: executes scripts, makes network calls, reads secrets. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free. This skill is licensed under MIT.
padosoft (@padosoft) published this skill. Their other Agent Skills are listed on their SkillMD profile.