Description
Add a first-party @comark/email renderer package that uses MJML under the hood to compile Comark Markdown documents (including frontmatter, components, and data bindings) into responsive, inline-styled HTML compatible with all major email clients.
Motivation
Currently, Comark supports multiple rendering targets (@comark/html, @comark/react, @comark/vue, @comark/svelte, @comark/angular and @comark/ansi). However, generating transactional emails that render consistently across major email clients (Gmail, Outlook, Apple Mail) requires specialized table-driven HTML and inline CSS.
Using raw @comark/html output for emails results in broken layouts because email clients strip modern CSS primitives like Flexbox, CSS Grid, external stylesheets, and custom web fonts. Users currently have to build custom post-processing pipelines with third-party tools like Juice or Maizzle to make Comark HTML email-safe.
With Comark's built-in data binding (::if conditionals and ::for loops) and component syntax, Markdown documents are an ideal authoring format for transactional email templates. We lack a first-party rendering package tailored for email client compatibility and first-class responsive email layout.
Proposed solution
Introduce @comark/email as an official workspace package. @comark/email will transform a Comark document AST into an MJML JSON tree, then compile it with mjml2html into inbox-ready HTML.
The package will map Comark AST nodes and frontmatter metadata into native MJML tags (mj-text, mj-button, mj-section, mj-column, mj-divider, and others). Email directives map directly to those MJML tags rather than emitting Tailwind HTML.
@comark/email is Node-first. MJML compilation runs on the server (Node.js). Live preview demos use a Vite server middleware that calls renderEmail rather than running MJML in the browser.
Key Features & Requirements
-
Frontmatter Configuration: Parse email metadata to configure subject, preview text, brand color, and theme options:
---
email:
subject: "Your order #{{ order.id }} has shipped!"
previewText: "Track your package delivery status."
brandColor: "#0066cc"
theme:
primary: "#0066cc"
background: "#f4f5f7"
---
-
Built-in Email Components & MJML Attributes: Support custom syntax for common email patterns with native MJML attributes:
::email-button{href="..." background-color="#0066cc" color="#ffffff"} → <mj-button>
::email-columns → <mj-section> with one <mj-column> per child
::email-divider{border-color="#cccccc"} → <mj-divider>
-
AST-to-MJML Transformation: Walk the Comark AST and emit an MJML JSON tree. Use @comark/html only for inner HTML of mj-text / mj-table / mj-raw (inline tags, lists, custom components).
-
MJML Compilation: Output fully compiled, inline-styled HTML ready to pass directly to email service providers (Resend, SendGrid, Postmark, AWS SES). Soft validation by default; errors returned in the result rather than thrown.
-
Integration with AST & Data Binding: Process AST output generated by comark and the binding plugin prior to compilation.
API Design Proposal
import { parseMarkdown } from 'comark'
import { renderEmail, renderEmailFromDocument } from '@comark/email'
const doc = await parseMarkdown(markdownText)
// Render to responsive, inbox-safe HTML string
const { html, subject, previewText, errors } = await renderEmailFromDocument(doc, {
email: {
brandColor: '#0066cc',
},
mjmlOptions: {
minify: true,
},
headCss: 'a { color: #0066cc; }',
})
// Or parse + render in one step
const result = await renderEmail(markdownText, {
email: { brandColor: '#0066cc' },
})
// Pass output directly to your email provider
await sendEmail({
to: user.email,
subject, // Extracted from frontmatter: "Your order #1234 has shipped!"
html, // Inline table-based HTML compiled by MJML
})
Render flow
Markdown → parseMarkdown → resolveEmailConfig
→ documentToMjmlJson (AST → MjmlNode)
→ compileMjml (mjml2html)
→ EmailRenderResult { html, subject, previewText, errors }
Alternatives Considered
- Maizzle: Tailwind-first PostHTML pipeline with strong CSS inlining. Rejected in favor of MJML's maintained semantic layout engine and native responsive email tags. Maizzle would also require Tailwind class authoring on email directives.
- React Email: Excellent component-driven ecosystem, but requires a React runtime. MJML provides a framework-agnostic pipeline better suited for
@comark/email across multi-framework backends (Node, Vue, Svelte, Angular).
- Low-Level CSS Inlining (Juice): Lightweight for single-column text, but fails to handle Outlook-specific conditional ghost tables (
<!--[if mso]>) for complex multi-column layouts automatically.
Additional context
Description
Add a first-party
@comark/emailrenderer package that uses MJML under the hood to compile Comark Markdown documents (including frontmatter, components, and data bindings) into responsive, inline-styled HTML compatible with all major email clients.Motivation
Currently, Comark supports multiple rendering targets (
@comark/html,@comark/react,@comark/vue,@comark/svelte,@comark/angularand@comark/ansi). However, generating transactional emails that render consistently across major email clients (Gmail, Outlook, Apple Mail) requires specialized table-driven HTML and inline CSS.Using raw
@comark/htmloutput for emails results in broken layouts because email clients strip modern CSS primitives like Flexbox, CSS Grid, external stylesheets, and custom web fonts. Users currently have to build custom post-processing pipelines with third-party tools like Juice or Maizzle to make Comark HTML email-safe.With Comark's built-in data binding (
::ifconditionals and::forloops) and component syntax, Markdown documents are an ideal authoring format for transactional email templates. We lack a first-party rendering package tailored for email client compatibility and first-class responsive email layout.Proposed solution
Introduce
@comark/emailas an official workspace package.@comark/emailwill transform a Comark document AST into an MJML JSON tree, then compile it withmjml2htmlinto inbox-ready HTML.The package will map Comark AST nodes and frontmatter metadata into native MJML tags (
mj-text,mj-button,mj-section,mj-column,mj-divider, and others). Email directives map directly to those MJML tags rather than emitting Tailwind HTML.@comark/emailis Node-first. MJML compilation runs on the server (Node.js). Live preview demos use a Vite server middleware that callsrenderEmailrather than running MJML in the browser.Key Features & Requirements
Frontmatter Configuration: Parse email metadata to configure subject, preview text, brand color, and theme options:
Built-in Email Components & MJML Attributes: Support custom syntax for common email patterns with native MJML attributes:
::email-button{href="..." background-color="#0066cc" color="#ffffff"}→<mj-button>::email-columns→<mj-section>with one<mj-column>per child::email-divider{border-color="#cccccc"}→<mj-divider>AST-to-MJML Transformation: Walk the Comark AST and emit an MJML JSON tree. Use
@comark/htmlonly for inner HTML ofmj-text/mj-table/mj-raw(inline tags, lists, custom components).MJML Compilation: Output fully compiled, inline-styled HTML ready to pass directly to email service providers (Resend, SendGrid, Postmark, AWS SES). Soft validation by default; errors returned in the result rather than thrown.
Integration with AST & Data Binding: Process AST output generated by
comarkand the binding plugin prior to compilation.API Design Proposal
Render flow
Alternatives Considered
@comark/emailacross multi-framework backends (Node, Vue, Svelte, Angular).<!--[if mso]>) for complex multi-column layouts automatically.Additional context