01 — Problem
HTML Emails Built by Hand, Every Single Time
Every campaign email I sent for workforce education programs followed the same ritual: open a previous email in the browser, view source, copy the HTML, find-and-replace the content, manually adjust spacing, test in 3 email clients, discover that Outlook had destroyed the layout, fix it, test again. A single branded email took 25–30 minutes. The formatting rules lived nowhere except in my head — which header font size, which button color, which footer disclaimer. When a colleague needed to send an email in my absence, they started from scratch because the “template” was whatever I’d built last time.
I needed a system where email content and email formatting were completely separated — where I could define the message in structured data and let the system handle rendering, CSS inlining, and client compatibility. A CLI command, not a design session.
02 — Architecture
Content In, HTML Out
The engine is a Python CLI with three processing stages:
Stage 1 — Content Definition (YAML/JSON)
Email content is defined as structured data: a YAML file specifying the subject line, header text, body blocks (text, buttons, images, dividers), and footer content. Each block type has a defined schema. This separation means the same content can render in different templates without modification, and non-technical users can author emails by editing a YAML file rather than touching HTML.
Stage 2 — Template Rendering (Jinja2)
Jinja2 templates define the visual structure: header layout, body block arrangement, button styling, and footer composition. The content YAML is merged with the template to produce raw HTML. Templates are modular — a header block, a CTA button block, and a text block can be composed independently, and the engine assembles them in the order specified by the content file.
Stage 3 — Production Processing (Premailer + Minification)
Premailer converts all CSS rules to inline styles — the only reliable way to ensure consistent rendering across email clients. Gmail strips <style> tags. Outlook ignores most CSS properties. Inlining every rule onto every element is ugly in source but necessary in practice. After inlining, the HTML is minified to reduce payload size for ESPs with message size limits.
Key Design Decisions
Why YAML content files instead of a visual editor? Visual editors produce inconsistent HTML that’s hard to version or diff. YAML files are plain text — they work with git, they’re easy to review, and they enforce structure by schema rather than by discipline. The tradeoff is a higher initial learning curve, but the consistency payoff is immediate.
Why Premailer instead of manual inlining? Manual CSS inlining is error-prone and doesn’t scale. Premailer automates the transformation deterministically. The same CSS rules produce the same inline styles every time, which eliminates the “it worked last time” debugging pattern that plagues manual email development.
03 — Outcomes
Measured Results
Time Reduction
email production from 25 minutes to under 2 minutes per campaign
Block Types
header, text, button, image, and footer — composable per email
CSS Inlined
automated via Premailer for cross-client compatibility
CLI Command
from YAML content to production-ready minified HTML
04 — Reflection
The Most Valuable Tool Is the Boring One
This is the least technically impressive project in my portfolio. There’s no AI, no distributed system, no complex architecture. It’s a templating engine with a CSS inliner. And it’s the tool I use most frequently — more than any agent pipeline or data intelligence system. The lesson is that leverage doesn’t correlate with complexity. A tool that saves 23 minutes per use, used 4 times per week, saves 80 hours per year. No agent system I’ve built comes close to that ROI.
What I’d change: I’d add a preview server that renders the email in-browser with hot reloading. Currently, testing requires running the CLI, opening the output file, and manually checking. A --preview flag that launches a local server with live reload would tighten the feedback loop from minutes to seconds.
“The projects that impress people and the projects that save time are almost never the same projects. Build for the latter.”
Outcomes
93% time reduction per email (25 min to <2 min); 5 composable block types; 100% automated CSS inlining; Single CLI command from content to production HTML