Abhishek E Tweb · email · bengaluru

home / projects / transactional-emails

Case 02 / 06 · Content-Type: multipart/alternative

The emails nobody praises — and everybody notices when they break.

Order confirmations, password resets, shipping updates: transactional email is infrastructure. This system replaced a drawer of one-off templates with an MJML component library — so every trigger renders consistently, and a change to the footer is one commit, not twelve.

Project
Email infrastructure
Role
Email developer
Timeline
2024 — present
Stack
MJML · HTML · Litmus · Postmark

The brief

Transactional messages are the highest-open-rate email a product sends, and the least glamorous to maintain. The starting state is familiar to anyone who has inherited an email stack: near-identical templates that had drifted apart — different footers, different button code, different dark-mode behaviour — because each had been edited in isolation. The brief: one system, every trigger, no drift.

The constraints

  • Rendering targets that disagree with each other. Outlook for Windows still uses Word's engine; Gmail clips at 102KB and strips <style> in some contexts; dark mode is implemented three different ways across clients.
  • Personalisation happens at send time. Templates carry merge fields and conditional blocks, so the compiled HTML has to stay valid around whatever the ESP injects.
  • Accessibility is non-negotiable for receipts — order tables must read as tables to screen readers, while layout tables must not.
  • Non-developers touch copy. Content edits shouldn't require understanding ghost tables.

The build

MJML as the abstraction layer. Writing <mj-section>/<mj-column> instead of nested tables moves Outlook conditional comments and hybrid-layout boilerplate into the compiler, where they're generated correctly every time. On top of it, a small component library: header, footer, order-summary table, bulletproof button, spacer system — each defined once, included everywhere.

  • Shared design tokens: colours, type scale and spacing live in one attributes file, so brand adjustments propagate through every template on the next build.
  • Semantic receipts: the compiled order table keeps real <th> semantics while everything structural is role="presentation" — the same split you can inspect in my order-confirmation template.
  • Dark-mode strategy: color-scheme metas, prefers-color-scheme overrides, and [data-ogsc] duplicates for Outlook.com's forced repaint — encoded once in the base layout.
  • A Litmus pass as the definition of done: the client matrix that matters here is Gmail (web + apps), Outlook Windows, Apple Mail/iOS and Samsung Mail. A template isn't shipped until the matrix is green.
The unglamorous win: plain-text parts, preheaders and List-Unsubscribe headers standardised across every trigger. Deliverability lives in these details more than in any design decision.

The outcome

New transactional templates start from components, not blank files — assembly instead of authorship. Cross-client bugs get fixed once, in the component, and disappear from every template at the next compile.