@lacspace/email-templates 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -6
- package/dist/index.cjs +3 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -104,18 +104,24 @@ await mail.send({ to, subject: "Your code", html: otpEmail({ code: "482913" }) }
|
|
|
104
104
|
|
|
105
105
|
## Licensing
|
|
106
106
|
|
|
107
|
-
This package is **free** under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** —
|
|
107
|
+
This package is **free** under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** — permissive freedoms. Use it in personal and commercial projects at no cost; just keep the notice.
|
|
108
108
|
|
|
109
109
|
Not every Lacspace package is free. We also offer **Commercial** (paid), **Client-specific**, and **Private** (proprietary) packages under separate terms. See the full **[Lacspace Licence Centre](https://lacspace.com/licenses)**.
|
|
110
110
|
|
|
111
|
+
<!-- LACSPACE-DEV-PLATFORM -->
|
|
112
|
+
|
|
111
113
|
---
|
|
112
114
|
|
|
113
|
-
|
|
115
|
+
## The Lacspace Developer Platform
|
|
114
116
|
|
|
115
|
-
|
|
117
|
+
`@lacspace/email-templates` is part of **63 zero-dependency, isomorphic TypeScript packages** — one standard library for the modern web. Explore the ecosystem:
|
|
116
118
|
|
|
117
|
-
|
|
119
|
+
- 📦 **This package, documented** — https://developer.lacspace.com/packages/email-templates
|
|
120
|
+
- 🗂️ **All 63 packages** — https://developer.lacspace.com/packages
|
|
121
|
+
- 🧭 **Developer handbook** — guides & runnable recipes — https://developer.lacspace.com/handbook
|
|
122
|
+
- 🧪 **Live playground** — run any package in your browser — https://developer.lacspace.com/playground
|
|
123
|
+
- 🖥️ **Finished app templates** — https://templates.lacspace.com
|
|
124
|
+
- 🚀 **Scaffold a full app** — `npm create lacspace-app@latest`
|
|
118
125
|
|
|
119
|
-
|
|
126
|
+
Free under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** — a permissive, free-to-use licence.
|
|
120
127
|
|
|
121
|
-
<div align="center"><sub>Built with care by <a href="https://lacspace.com">Lacspace</a> · Lacspace Free Licence · <a href="https://github.com/lacspace/npm-packages">source</a></sub></div>
|
package/dist/index.cjs
CHANGED
|
@@ -20,12 +20,13 @@ function theme(t) {
|
|
|
20
20
|
function heading(text2, opts = {}) {
|
|
21
21
|
const t = theme(opts.theme);
|
|
22
22
|
const size = opts.level === 3 ? 18 : opts.level === 2 ? 22 : 26;
|
|
23
|
-
return `<h1 style="margin:0 0 12px;font-family:${t.fontFamily};font-size:${size}px;line-height:1.3;font-weight:700;color:${t.textColor};">${escapeHtml(text2)}</h1>`;
|
|
23
|
+
return `<h1 class="t" style="margin:0 0 12px;font-family:${t.fontFamily};font-size:${size}px;line-height:1.3;font-weight:700;color:${t.textColor};">${escapeHtml(text2)}</h1>`;
|
|
24
24
|
}
|
|
25
25
|
function text(content, opts = {}) {
|
|
26
26
|
const t = theme(opts.theme);
|
|
27
27
|
const color = opts.muted ? t.mutedColor : t.textColor;
|
|
28
|
-
|
|
28
|
+
const cls = opts.muted ? "" : ` class="t"`;
|
|
29
|
+
return `<p${cls} style="margin:0 0 16px;font-family:${t.fontFamily};font-size:15px;line-height:1.6;color:${color};">${escapeHtml(content)}</p>`;
|
|
29
30
|
}
|
|
30
31
|
function html(raw) {
|
|
31
32
|
return raw;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["text"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @lacspace/email-templates\n * Compose bulletproof, responsive HTML emails from simple blocks.\n *\n * No more hand-writing <table> soup. Components return email-client-safe HTML\n * with inline styles; `render()` wraps them in a responsive, dark-mode-aware\n * layout. Ships with ready-made OTP / welcome / alert / invoice templates.\n *\n * Zero dependencies · isomorphic · fully typed.\n */\n\nexport interface Theme {\n brandColor: string;\n bg: string;\n cardBg: string;\n textColor: string;\n mutedColor: string;\n borderColor: string;\n fontFamily: string;\n borderRadius: string;\n}\n\nexport const defaultTheme: Theme = {\n brandColor: \"#2563eb\",\n bg: \"#f4f5f7\",\n cardBg: \"#ffffff\",\n textColor: \"#1f2937\",\n mutedColor: \"#6b7280\",\n borderColor: \"#e5e7eb\",\n fontFamily:\n \"-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif\",\n borderRadius: \"12px\",\n};\n\n/** Escape untrusted text before placing it in HTML. */\nexport function escapeHtml(input: string): string {\n return String(input)\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction theme(t?: Partial<Theme>): Theme {\n return { ...defaultTheme, ...t };\n}\n\n/* ------------------------------------------------------------------ *\n * Blocks — each returns an HTML string. Compose them into render().\n * ------------------------------------------------------------------ */\n\nexport function heading(text: string, opts: { level?: 1 | 2 | 3; theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const size = opts.level === 3 ? 18 : opts.level === 2 ? 22 : 26;\n return `<h1 style=\"margin:0 0 12px;font-family:${t.fontFamily};font-size:${size}px;line-height:1.3;font-weight:700;color:${t.textColor};\">${escapeHtml(text)}</h1>`;\n}\n\nexport function text(content: string, opts: { muted?: boolean; theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const color = opts.muted ? t.mutedColor : t.textColor;\n return `<p style=\"margin:0 0 16px;font-family:${t.fontFamily};font-size:15px;line-height:1.6;color:${color};\">${escapeHtml(content)}</p>`;\n}\n\n/** Raw HTML passthrough — you are responsible for escaping. */\nexport function html(raw: string): string {\n return raw;\n}\n\n/** A bulletproof, table-based CTA button that renders in Outlook too. */\nexport function button(\n label: string,\n href: string,\n opts: { theme?: Partial<Theme> } = {},\n): string {\n const t = theme(opts.theme);\n const safeHref = escapeHtml(href);\n return `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 0 20px;\"><tr><td align=\"center\" bgcolor=\"${t.brandColor}\" style=\"border-radius:8px;\">\n<a href=\"${safeHref}\" target=\"_blank\" style=\"display:inline-block;padding:12px 28px;font-family:${t.fontFamily};font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:8px;background:${t.brandColor};\">${escapeHtml(label)}</a>\n</td></tr></table>`;\n}\n\n/** A large, letter-spaced code block — perfect for OTPs. */\nexport function code(value: string, opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n return `<div style=\"margin:0 0 20px;padding:16px;text-align:center;background:${t.bg};border:1px solid ${t.borderColor};border-radius:${t.borderRadius};font-family:'SFMono-Regular',Consolas,Menlo,monospace;font-size:32px;font-weight:700;letter-spacing:8px;color:${t.textColor};\">${escapeHtml(value)}</div>`;\n}\n\nexport function divider(opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n return `<hr style=\"border:none;border-top:1px solid ${t.borderColor};margin:24px 0;\" />`;\n}\n\nexport function spacer(height = 16): string {\n return `<div style=\"line-height:${height}px;height:${height}px;\"> </div>`;\n}\n\nexport function image(src: string, opts: { alt?: string; width?: number } = {}): string {\n const w = opts.width ? `width=\"${opts.width}\" style=\"max-width:100%;height:auto;display:block;margin:0 auto;\"` : `style=\"max-width:100%;height:auto;display:block;margin:0 auto;\"`;\n return `<img src=\"${escapeHtml(src)}\" alt=\"${escapeHtml(opts.alt ?? \"\")}\" ${w} />`;\n}\n\nexport function list(items: string[], opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const lis = items\n .map(\n (i) =>\n `<li style=\"margin:0 0 8px;font-family:${t.fontFamily};font-size:15px;line-height:1.6;color:${t.textColor};\">${escapeHtml(i)}</li>`,\n )\n .join(\"\");\n return `<ul style=\"margin:0 0 16px;padding-left:20px;\">${lis}</ul>`;\n}\n\n/** A simple two-column key/value table — great for invoices & receipts. */\nexport function keyValue(\n rows: [string, string][],\n opts: { theme?: Partial<Theme> } = {},\n): string {\n const t = theme(opts.theme);\n const body = rows\n .map(\n ([k, v]) =>\n `<tr><td style=\"padding:8px 0;font-family:${t.fontFamily};font-size:14px;color:${t.mutedColor};\">${escapeHtml(k)}</td><td align=\"right\" style=\"padding:8px 0;font-family:${t.fontFamily};font-size:14px;font-weight:600;color:${t.textColor};\">${escapeHtml(v)}</td></tr>`,\n )\n .join(\"\");\n return `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:0 0 16px;border-top:1px solid ${t.borderColor};\">${body}</table>`;\n}\n\n/* ------------------------------------------------------------------ *\n * Layout\n * ------------------------------------------------------------------ */\n\nexport interface EmailOptions {\n title?: string;\n /** Hidden inbox-preview text. */\n preheader?: string;\n theme?: Partial<Theme>;\n /** Footer HTML/text (already trusted). */\n footer?: string;\n brandName?: string;\n logoUrl?: string;\n width?: number;\n}\n\n/** Wrap composed blocks in a responsive, email-client-safe document. */\nexport function render(opts: EmailOptions, blocks: string[]): string {\n const t = theme(opts.theme);\n const width = opts.width ?? 600;\n const preheader = opts.preheader\n ? `<div style=\"display:none;max-height:0;overflow:hidden;opacity:0;\">${escapeHtml(opts.preheader)}</div>`\n : \"\";\n const brand = opts.logoUrl\n ? `<img src=\"${escapeHtml(opts.logoUrl)}\" alt=\"${escapeHtml(opts.brandName ?? \"\")}\" height=\"32\" style=\"display:block;height:32px;\" />`\n : opts.brandName\n ? `<span style=\"font-family:${t.fontFamily};font-size:18px;font-weight:700;color:${t.brandColor};\">${escapeHtml(opts.brandName)}</span>`\n : \"\";\n const header = brand ? `<tr><td style=\"padding:0 0 20px;\">${brand}</td></tr>` : \"\";\n const footer = opts.footer\n ? `<tr><td style=\"padding:24px 8px 0;font-family:${t.fontFamily};font-size:12px;line-height:1.6;color:${t.mutedColor};text-align:center;\">${opts.footer}</td></tr>`\n : \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<meta name=\"x-apple-disable-message-reformatting\" />\n<title>${escapeHtml(opts.title ?? \"\")}</title>\n<style>\n @media only screen and (max-width:600px){ .container{width:100%!important;} .px{padding-left:20px!important;padding-right:20px!important;} }\n @media (prefers-color-scheme: dark){ body,.bg{background:#0b0b12!important;} .card{background:#16161f!important;} .t{color:#e5e7eb!important;} }\n</style>\n</head>\n<body class=\"bg\" style=\"margin:0;padding:0;background:${t.bg};\">\n${preheader}\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" class=\"bg\" style=\"background:${t.bg};padding:32px 0;\">\n<tr><td align=\"center\">\n<table role=\"presentation\" width=\"${width}\" class=\"container\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:${width}px;max-width:${width}px;\">\n${header}\n<tr><td class=\"card px\" style=\"background:${t.cardBg};border:1px solid ${t.borderColor};border-radius:${t.borderRadius};padding:32px;\">\n${blocks.join(\"\\n\")}\n</td></tr>\n${footer}\n</table>\n</td></tr>\n</table>\n</body>\n</html>`;\n}\n\n/* ------------------------------------------------------------------ *\n * Ready-made templates\n * ------------------------------------------------------------------ */\n\nexport interface BrandInfo {\n brandName?: string;\n logoUrl?: string;\n theme?: Partial<Theme>;\n footer?: string;\n}\n\n/** One-time-passcode email. */\nexport function otpEmail(\n o: { code: string; heading?: string; message?: string; expiresMinutes?: number } & BrandInfo,\n): string {\n return render(\n { title: o.heading ?? \"Your verification code\", preheader: `Your code is ${o.code}`, ...o },\n [\n heading(o.heading ?? \"Verify your email\", { theme: o.theme }),\n text(o.message ?? \"Use the code below to continue. Never share it with anyone.\", {\n theme: o.theme,\n }),\n code(o.code, { theme: o.theme }),\n text(\n `This code expires in ${o.expiresMinutes ?? 10} minutes.`,\n { muted: true, theme: o.theme },\n ),\n ],\n );\n}\n\n/** Welcome / onboarding email with a CTA. */\nexport function welcomeEmail(\n o: { name?: string; message?: string; ctaLabel?: string; ctaHref?: string } & BrandInfo,\n): string {\n const blocks = [\n heading(o.name ? `Welcome, ${o.name}!` : \"Welcome aboard!\", { theme: o.theme }),\n text(o.message ?? \"We're thrilled to have you. Let's get you started.\", { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"Get started\", o.ctaHref, { theme: o.theme }));\n return render({ title: \"Welcome\", preheader: o.message, ...o }, blocks);\n}\n\n/** Alert / notification email. */\nexport function alertEmail(\n o: { title: string; message: string; ctaLabel?: string; ctaHref?: string } & BrandInfo,\n): string {\n const blocks = [\n heading(o.title, { theme: o.theme }),\n text(o.message, { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"View details\", o.ctaHref, { theme: o.theme }));\n return render({ preheader: o.message, ...o }, blocks);\n}\n\n/** Invoice / receipt email. */\nexport function invoiceEmail(\n o: {\n heading?: string;\n intro?: string;\n rows: [string, string][];\n total?: [string, string];\n ctaLabel?: string;\n ctaHref?: string;\n } & BrandInfo,\n): string {\n const blocks = [\n heading(o.heading ?? \"Payment receipt\", { theme: o.theme }),\n text(o.intro ?? \"Thanks for your payment. Here's your receipt.\", { theme: o.theme }),\n keyValue(o.total ? [...o.rows, o.total] : o.rows, { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"View invoice\", o.ctaHref, { theme: o.theme }));\n return render({ title: o.heading ?? \"Receipt\", ...o }, blocks);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["text"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @lacspace/email-templates\n * Compose bulletproof, responsive HTML emails from simple blocks.\n *\n * No more hand-writing <table> soup. Components return email-client-safe HTML\n * with inline styles; `render()` wraps them in a responsive, dark-mode-aware\n * layout. Ships with ready-made OTP / welcome / alert / invoice templates.\n *\n * Zero dependencies · isomorphic · fully typed.\n */\n\nexport interface Theme {\n brandColor: string;\n bg: string;\n cardBg: string;\n textColor: string;\n mutedColor: string;\n borderColor: string;\n fontFamily: string;\n borderRadius: string;\n}\n\nexport const defaultTheme: Theme = {\n brandColor: \"#2563eb\",\n bg: \"#f4f5f7\",\n cardBg: \"#ffffff\",\n textColor: \"#1f2937\",\n mutedColor: \"#6b7280\",\n borderColor: \"#e5e7eb\",\n fontFamily:\n \"-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif\",\n borderRadius: \"12px\",\n};\n\n/** Escape untrusted text before placing it in HTML. */\nexport function escapeHtml(input: string): string {\n return String(input)\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction theme(t?: Partial<Theme>): Theme {\n return { ...defaultTheme, ...t };\n}\n\n/* ------------------------------------------------------------------ *\n * Blocks — each returns an HTML string. Compose them into render().\n * ------------------------------------------------------------------ */\n\nexport function heading(text: string, opts: { level?: 1 | 2 | 3; theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const size = opts.level === 3 ? 18 : opts.level === 2 ? 22 : 26;\n return `<h1 class=\"t\" style=\"margin:0 0 12px;font-family:${t.fontFamily};font-size:${size}px;line-height:1.3;font-weight:700;color:${t.textColor};\">${escapeHtml(text)}</h1>`;\n}\n\nexport function text(content: string, opts: { muted?: boolean; theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const color = opts.muted ? t.mutedColor : t.textColor;\n // Only primary body text carries `.t`; muted text keeps its lighter grey, which\n // already reads on a dark background (see the dark-mode @media rule in render()).\n const cls = opts.muted ? \"\" : ` class=\"t\"`;\n return `<p${cls} style=\"margin:0 0 16px;font-family:${t.fontFamily};font-size:15px;line-height:1.6;color:${color};\">${escapeHtml(content)}</p>`;\n}\n\n/** Raw HTML passthrough — you are responsible for escaping. */\nexport function html(raw: string): string {\n return raw;\n}\n\n/** A bulletproof, table-based CTA button that renders in Outlook too. */\nexport function button(\n label: string,\n href: string,\n opts: { theme?: Partial<Theme> } = {},\n): string {\n const t = theme(opts.theme);\n const safeHref = escapeHtml(href);\n return `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 0 20px;\"><tr><td align=\"center\" bgcolor=\"${t.brandColor}\" style=\"border-radius:8px;\">\n<a href=\"${safeHref}\" target=\"_blank\" style=\"display:inline-block;padding:12px 28px;font-family:${t.fontFamily};font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:8px;background:${t.brandColor};\">${escapeHtml(label)}</a>\n</td></tr></table>`;\n}\n\n/** A large, letter-spaced code block — perfect for OTPs. */\nexport function code(value: string, opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n return `<div style=\"margin:0 0 20px;padding:16px;text-align:center;background:${t.bg};border:1px solid ${t.borderColor};border-radius:${t.borderRadius};font-family:'SFMono-Regular',Consolas,Menlo,monospace;font-size:32px;font-weight:700;letter-spacing:8px;color:${t.textColor};\">${escapeHtml(value)}</div>`;\n}\n\nexport function divider(opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n return `<hr style=\"border:none;border-top:1px solid ${t.borderColor};margin:24px 0;\" />`;\n}\n\nexport function spacer(height = 16): string {\n return `<div style=\"line-height:${height}px;height:${height}px;\"> </div>`;\n}\n\nexport function image(src: string, opts: { alt?: string; width?: number } = {}): string {\n const w = opts.width ? `width=\"${opts.width}\" style=\"max-width:100%;height:auto;display:block;margin:0 auto;\"` : `style=\"max-width:100%;height:auto;display:block;margin:0 auto;\"`;\n return `<img src=\"${escapeHtml(src)}\" alt=\"${escapeHtml(opts.alt ?? \"\")}\" ${w} />`;\n}\n\nexport function list(items: string[], opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const lis = items\n .map(\n (i) =>\n `<li style=\"margin:0 0 8px;font-family:${t.fontFamily};font-size:15px;line-height:1.6;color:${t.textColor};\">${escapeHtml(i)}</li>`,\n )\n .join(\"\");\n return `<ul style=\"margin:0 0 16px;padding-left:20px;\">${lis}</ul>`;\n}\n\n/** A simple two-column key/value table — great for invoices & receipts. */\nexport function keyValue(\n rows: [string, string][],\n opts: { theme?: Partial<Theme> } = {},\n): string {\n const t = theme(opts.theme);\n const body = rows\n .map(\n ([k, v]) =>\n `<tr><td style=\"padding:8px 0;font-family:${t.fontFamily};font-size:14px;color:${t.mutedColor};\">${escapeHtml(k)}</td><td align=\"right\" style=\"padding:8px 0;font-family:${t.fontFamily};font-size:14px;font-weight:600;color:${t.textColor};\">${escapeHtml(v)}</td></tr>`,\n )\n .join(\"\");\n return `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:0 0 16px;border-top:1px solid ${t.borderColor};\">${body}</table>`;\n}\n\n/* ------------------------------------------------------------------ *\n * Layout\n * ------------------------------------------------------------------ */\n\nexport interface EmailOptions {\n title?: string;\n /** Hidden inbox-preview text. */\n preheader?: string;\n theme?: Partial<Theme>;\n /** Footer HTML/text (already trusted). */\n footer?: string;\n brandName?: string;\n logoUrl?: string;\n width?: number;\n}\n\n/** Wrap composed blocks in a responsive, email-client-safe document. */\nexport function render(opts: EmailOptions, blocks: string[]): string {\n const t = theme(opts.theme);\n const width = opts.width ?? 600;\n const preheader = opts.preheader\n ? `<div style=\"display:none;max-height:0;overflow:hidden;opacity:0;\">${escapeHtml(opts.preheader)}</div>`\n : \"\";\n const brand = opts.logoUrl\n ? `<img src=\"${escapeHtml(opts.logoUrl)}\" alt=\"${escapeHtml(opts.brandName ?? \"\")}\" height=\"32\" style=\"display:block;height:32px;\" />`\n : opts.brandName\n ? `<span style=\"font-family:${t.fontFamily};font-size:18px;font-weight:700;color:${t.brandColor};\">${escapeHtml(opts.brandName)}</span>`\n : \"\";\n const header = brand ? `<tr><td style=\"padding:0 0 20px;\">${brand}</td></tr>` : \"\";\n const footer = opts.footer\n ? `<tr><td style=\"padding:24px 8px 0;font-family:${t.fontFamily};font-size:12px;line-height:1.6;color:${t.mutedColor};text-align:center;\">${opts.footer}</td></tr>`\n : \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<meta name=\"x-apple-disable-message-reformatting\" />\n<title>${escapeHtml(opts.title ?? \"\")}</title>\n<style>\n @media only screen and (max-width:600px){ .container{width:100%!important;} .px{padding-left:20px!important;padding-right:20px!important;} }\n @media (prefers-color-scheme: dark){ body,.bg{background:#0b0b12!important;} .card{background:#16161f!important;} .t{color:#e5e7eb!important;} }\n</style>\n</head>\n<body class=\"bg\" style=\"margin:0;padding:0;background:${t.bg};\">\n${preheader}\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" class=\"bg\" style=\"background:${t.bg};padding:32px 0;\">\n<tr><td align=\"center\">\n<table role=\"presentation\" width=\"${width}\" class=\"container\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:${width}px;max-width:${width}px;\">\n${header}\n<tr><td class=\"card px\" style=\"background:${t.cardBg};border:1px solid ${t.borderColor};border-radius:${t.borderRadius};padding:32px;\">\n${blocks.join(\"\\n\")}\n</td></tr>\n${footer}\n</table>\n</td></tr>\n</table>\n</body>\n</html>`;\n}\n\n/* ------------------------------------------------------------------ *\n * Ready-made templates\n * ------------------------------------------------------------------ */\n\nexport interface BrandInfo {\n brandName?: string;\n logoUrl?: string;\n theme?: Partial<Theme>;\n footer?: string;\n}\n\n/** One-time-passcode email. */\nexport function otpEmail(\n o: { code: string; heading?: string; message?: string; expiresMinutes?: number } & BrandInfo,\n): string {\n return render(\n { title: o.heading ?? \"Your verification code\", preheader: `Your code is ${o.code}`, ...o },\n [\n heading(o.heading ?? \"Verify your email\", { theme: o.theme }),\n text(o.message ?? \"Use the code below to continue. Never share it with anyone.\", {\n theme: o.theme,\n }),\n code(o.code, { theme: o.theme }),\n text(\n `This code expires in ${o.expiresMinutes ?? 10} minutes.`,\n { muted: true, theme: o.theme },\n ),\n ],\n );\n}\n\n/** Welcome / onboarding email with a CTA. */\nexport function welcomeEmail(\n o: { name?: string; message?: string; ctaLabel?: string; ctaHref?: string } & BrandInfo,\n): string {\n const blocks = [\n heading(o.name ? `Welcome, ${o.name}!` : \"Welcome aboard!\", { theme: o.theme }),\n text(o.message ?? \"We're thrilled to have you. Let's get you started.\", { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"Get started\", o.ctaHref, { theme: o.theme }));\n return render({ title: \"Welcome\", preheader: o.message, ...o }, blocks);\n}\n\n/** Alert / notification email. */\nexport function alertEmail(\n o: { title: string; message: string; ctaLabel?: string; ctaHref?: string } & BrandInfo,\n): string {\n const blocks = [\n heading(o.title, { theme: o.theme }),\n text(o.message, { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"View details\", o.ctaHref, { theme: o.theme }));\n return render({ preheader: o.message, ...o }, blocks);\n}\n\n/** Invoice / receipt email. */\nexport function invoiceEmail(\n o: {\n heading?: string;\n intro?: string;\n rows: [string, string][];\n total?: [string, string];\n ctaLabel?: string;\n ctaHref?: string;\n } & BrandInfo,\n): string {\n const blocks = [\n heading(o.heading ?? \"Payment receipt\", { theme: o.theme }),\n text(o.intro ?? \"Thanks for your payment. Here's your receipt.\", { theme: o.theme }),\n keyValue(o.total ? [...o.rows, o.total] : o.rows, { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"View invoice\", o.ctaHref, { theme: o.theme }));\n return render({ title: o.heading ?? \"Receipt\", ...o }, blocks);\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -18,12 +18,13 @@ function theme(t) {
|
|
|
18
18
|
function heading(text2, opts = {}) {
|
|
19
19
|
const t = theme(opts.theme);
|
|
20
20
|
const size = opts.level === 3 ? 18 : opts.level === 2 ? 22 : 26;
|
|
21
|
-
return `<h1 style="margin:0 0 12px;font-family:${t.fontFamily};font-size:${size}px;line-height:1.3;font-weight:700;color:${t.textColor};">${escapeHtml(text2)}</h1>`;
|
|
21
|
+
return `<h1 class="t" style="margin:0 0 12px;font-family:${t.fontFamily};font-size:${size}px;line-height:1.3;font-weight:700;color:${t.textColor};">${escapeHtml(text2)}</h1>`;
|
|
22
22
|
}
|
|
23
23
|
function text(content, opts = {}) {
|
|
24
24
|
const t = theme(opts.theme);
|
|
25
25
|
const color = opts.muted ? t.mutedColor : t.textColor;
|
|
26
|
-
|
|
26
|
+
const cls = opts.muted ? "" : ` class="t"`;
|
|
27
|
+
return `<p${cls} style="margin:0 0 16px;font-family:${t.fontFamily};font-size:15px;line-height:1.6;color:${color};">${escapeHtml(content)}</p>`;
|
|
27
28
|
}
|
|
28
29
|
function html(raw) {
|
|
29
30
|
return raw;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["text"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @lacspace/email-templates\n * Compose bulletproof, responsive HTML emails from simple blocks.\n *\n * No more hand-writing <table> soup. Components return email-client-safe HTML\n * with inline styles; `render()` wraps them in a responsive, dark-mode-aware\n * layout. Ships with ready-made OTP / welcome / alert / invoice templates.\n *\n * Zero dependencies · isomorphic · fully typed.\n */\n\nexport interface Theme {\n brandColor: string;\n bg: string;\n cardBg: string;\n textColor: string;\n mutedColor: string;\n borderColor: string;\n fontFamily: string;\n borderRadius: string;\n}\n\nexport const defaultTheme: Theme = {\n brandColor: \"#2563eb\",\n bg: \"#f4f5f7\",\n cardBg: \"#ffffff\",\n textColor: \"#1f2937\",\n mutedColor: \"#6b7280\",\n borderColor: \"#e5e7eb\",\n fontFamily:\n \"-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif\",\n borderRadius: \"12px\",\n};\n\n/** Escape untrusted text before placing it in HTML. */\nexport function escapeHtml(input: string): string {\n return String(input)\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction theme(t?: Partial<Theme>): Theme {\n return { ...defaultTheme, ...t };\n}\n\n/* ------------------------------------------------------------------ *\n * Blocks — each returns an HTML string. Compose them into render().\n * ------------------------------------------------------------------ */\n\nexport function heading(text: string, opts: { level?: 1 | 2 | 3; theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const size = opts.level === 3 ? 18 : opts.level === 2 ? 22 : 26;\n return `<h1 style=\"margin:0 0 12px;font-family:${t.fontFamily};font-size:${size}px;line-height:1.3;font-weight:700;color:${t.textColor};\">${escapeHtml(text)}</h1>`;\n}\n\nexport function text(content: string, opts: { muted?: boolean; theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const color = opts.muted ? t.mutedColor : t.textColor;\n return `<p style=\"margin:0 0 16px;font-family:${t.fontFamily};font-size:15px;line-height:1.6;color:${color};\">${escapeHtml(content)}</p>`;\n}\n\n/** Raw HTML passthrough — you are responsible for escaping. */\nexport function html(raw: string): string {\n return raw;\n}\n\n/** A bulletproof, table-based CTA button that renders in Outlook too. */\nexport function button(\n label: string,\n href: string,\n opts: { theme?: Partial<Theme> } = {},\n): string {\n const t = theme(opts.theme);\n const safeHref = escapeHtml(href);\n return `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 0 20px;\"><tr><td align=\"center\" bgcolor=\"${t.brandColor}\" style=\"border-radius:8px;\">\n<a href=\"${safeHref}\" target=\"_blank\" style=\"display:inline-block;padding:12px 28px;font-family:${t.fontFamily};font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:8px;background:${t.brandColor};\">${escapeHtml(label)}</a>\n</td></tr></table>`;\n}\n\n/** A large, letter-spaced code block — perfect for OTPs. */\nexport function code(value: string, opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n return `<div style=\"margin:0 0 20px;padding:16px;text-align:center;background:${t.bg};border:1px solid ${t.borderColor};border-radius:${t.borderRadius};font-family:'SFMono-Regular',Consolas,Menlo,monospace;font-size:32px;font-weight:700;letter-spacing:8px;color:${t.textColor};\">${escapeHtml(value)}</div>`;\n}\n\nexport function divider(opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n return `<hr style=\"border:none;border-top:1px solid ${t.borderColor};margin:24px 0;\" />`;\n}\n\nexport function spacer(height = 16): string {\n return `<div style=\"line-height:${height}px;height:${height}px;\"> </div>`;\n}\n\nexport function image(src: string, opts: { alt?: string; width?: number } = {}): string {\n const w = opts.width ? `width=\"${opts.width}\" style=\"max-width:100%;height:auto;display:block;margin:0 auto;\"` : `style=\"max-width:100%;height:auto;display:block;margin:0 auto;\"`;\n return `<img src=\"${escapeHtml(src)}\" alt=\"${escapeHtml(opts.alt ?? \"\")}\" ${w} />`;\n}\n\nexport function list(items: string[], opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const lis = items\n .map(\n (i) =>\n `<li style=\"margin:0 0 8px;font-family:${t.fontFamily};font-size:15px;line-height:1.6;color:${t.textColor};\">${escapeHtml(i)}</li>`,\n )\n .join(\"\");\n return `<ul style=\"margin:0 0 16px;padding-left:20px;\">${lis}</ul>`;\n}\n\n/** A simple two-column key/value table — great for invoices & receipts. */\nexport function keyValue(\n rows: [string, string][],\n opts: { theme?: Partial<Theme> } = {},\n): string {\n const t = theme(opts.theme);\n const body = rows\n .map(\n ([k, v]) =>\n `<tr><td style=\"padding:8px 0;font-family:${t.fontFamily};font-size:14px;color:${t.mutedColor};\">${escapeHtml(k)}</td><td align=\"right\" style=\"padding:8px 0;font-family:${t.fontFamily};font-size:14px;font-weight:600;color:${t.textColor};\">${escapeHtml(v)}</td></tr>`,\n )\n .join(\"\");\n return `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:0 0 16px;border-top:1px solid ${t.borderColor};\">${body}</table>`;\n}\n\n/* ------------------------------------------------------------------ *\n * Layout\n * ------------------------------------------------------------------ */\n\nexport interface EmailOptions {\n title?: string;\n /** Hidden inbox-preview text. */\n preheader?: string;\n theme?: Partial<Theme>;\n /** Footer HTML/text (already trusted). */\n footer?: string;\n brandName?: string;\n logoUrl?: string;\n width?: number;\n}\n\n/** Wrap composed blocks in a responsive, email-client-safe document. */\nexport function render(opts: EmailOptions, blocks: string[]): string {\n const t = theme(opts.theme);\n const width = opts.width ?? 600;\n const preheader = opts.preheader\n ? `<div style=\"display:none;max-height:0;overflow:hidden;opacity:0;\">${escapeHtml(opts.preheader)}</div>`\n : \"\";\n const brand = opts.logoUrl\n ? `<img src=\"${escapeHtml(opts.logoUrl)}\" alt=\"${escapeHtml(opts.brandName ?? \"\")}\" height=\"32\" style=\"display:block;height:32px;\" />`\n : opts.brandName\n ? `<span style=\"font-family:${t.fontFamily};font-size:18px;font-weight:700;color:${t.brandColor};\">${escapeHtml(opts.brandName)}</span>`\n : \"\";\n const header = brand ? `<tr><td style=\"padding:0 0 20px;\">${brand}</td></tr>` : \"\";\n const footer = opts.footer\n ? `<tr><td style=\"padding:24px 8px 0;font-family:${t.fontFamily};font-size:12px;line-height:1.6;color:${t.mutedColor};text-align:center;\">${opts.footer}</td></tr>`\n : \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<meta name=\"x-apple-disable-message-reformatting\" />\n<title>${escapeHtml(opts.title ?? \"\")}</title>\n<style>\n @media only screen and (max-width:600px){ .container{width:100%!important;} .px{padding-left:20px!important;padding-right:20px!important;} }\n @media (prefers-color-scheme: dark){ body,.bg{background:#0b0b12!important;} .card{background:#16161f!important;} .t{color:#e5e7eb!important;} }\n</style>\n</head>\n<body class=\"bg\" style=\"margin:0;padding:0;background:${t.bg};\">\n${preheader}\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" class=\"bg\" style=\"background:${t.bg};padding:32px 0;\">\n<tr><td align=\"center\">\n<table role=\"presentation\" width=\"${width}\" class=\"container\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:${width}px;max-width:${width}px;\">\n${header}\n<tr><td class=\"card px\" style=\"background:${t.cardBg};border:1px solid ${t.borderColor};border-radius:${t.borderRadius};padding:32px;\">\n${blocks.join(\"\\n\")}\n</td></tr>\n${footer}\n</table>\n</td></tr>\n</table>\n</body>\n</html>`;\n}\n\n/* ------------------------------------------------------------------ *\n * Ready-made templates\n * ------------------------------------------------------------------ */\n\nexport interface BrandInfo {\n brandName?: string;\n logoUrl?: string;\n theme?: Partial<Theme>;\n footer?: string;\n}\n\n/** One-time-passcode email. */\nexport function otpEmail(\n o: { code: string; heading?: string; message?: string; expiresMinutes?: number } & BrandInfo,\n): string {\n return render(\n { title: o.heading ?? \"Your verification code\", preheader: `Your code is ${o.code}`, ...o },\n [\n heading(o.heading ?? \"Verify your email\", { theme: o.theme }),\n text(o.message ?? \"Use the code below to continue. Never share it with anyone.\", {\n theme: o.theme,\n }),\n code(o.code, { theme: o.theme }),\n text(\n `This code expires in ${o.expiresMinutes ?? 10} minutes.`,\n { muted: true, theme: o.theme },\n ),\n ],\n );\n}\n\n/** Welcome / onboarding email with a CTA. */\nexport function welcomeEmail(\n o: { name?: string; message?: string; ctaLabel?: string; ctaHref?: string } & BrandInfo,\n): string {\n const blocks = [\n heading(o.name ? `Welcome, ${o.name}!` : \"Welcome aboard!\", { theme: o.theme }),\n text(o.message ?? \"We're thrilled to have you. Let's get you started.\", { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"Get started\", o.ctaHref, { theme: o.theme }));\n return render({ title: \"Welcome\", preheader: o.message, ...o }, blocks);\n}\n\n/** Alert / notification email. */\nexport function alertEmail(\n o: { title: string; message: string; ctaLabel?: string; ctaHref?: string } & BrandInfo,\n): string {\n const blocks = [\n heading(o.title, { theme: o.theme }),\n text(o.message, { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"View details\", o.ctaHref, { theme: o.theme }));\n return render({ preheader: o.message, ...o }, blocks);\n}\n\n/** Invoice / receipt email. */\nexport function invoiceEmail(\n o: {\n heading?: string;\n intro?: string;\n rows: [string, string][];\n total?: [string, string];\n ctaLabel?: string;\n ctaHref?: string;\n } & BrandInfo,\n): string {\n const blocks = [\n heading(o.heading ?? \"Payment receipt\", { theme: o.theme }),\n text(o.intro ?? \"Thanks for your payment. Here's your receipt.\", { theme: o.theme }),\n keyValue(o.total ? [...o.rows, o.total] : o.rows, { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"View invoice\", o.ctaHref, { theme: o.theme }));\n return render({ title: o.heading ?? \"Receipt\", ...o }, blocks);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["text"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @lacspace/email-templates\n * Compose bulletproof, responsive HTML emails from simple blocks.\n *\n * No more hand-writing <table> soup. Components return email-client-safe HTML\n * with inline styles; `render()` wraps them in a responsive, dark-mode-aware\n * layout. Ships with ready-made OTP / welcome / alert / invoice templates.\n *\n * Zero dependencies · isomorphic · fully typed.\n */\n\nexport interface Theme {\n brandColor: string;\n bg: string;\n cardBg: string;\n textColor: string;\n mutedColor: string;\n borderColor: string;\n fontFamily: string;\n borderRadius: string;\n}\n\nexport const defaultTheme: Theme = {\n brandColor: \"#2563eb\",\n bg: \"#f4f5f7\",\n cardBg: \"#ffffff\",\n textColor: \"#1f2937\",\n mutedColor: \"#6b7280\",\n borderColor: \"#e5e7eb\",\n fontFamily:\n \"-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif\",\n borderRadius: \"12px\",\n};\n\n/** Escape untrusted text before placing it in HTML. */\nexport function escapeHtml(input: string): string {\n return String(input)\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction theme(t?: Partial<Theme>): Theme {\n return { ...defaultTheme, ...t };\n}\n\n/* ------------------------------------------------------------------ *\n * Blocks — each returns an HTML string. Compose them into render().\n * ------------------------------------------------------------------ */\n\nexport function heading(text: string, opts: { level?: 1 | 2 | 3; theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const size = opts.level === 3 ? 18 : opts.level === 2 ? 22 : 26;\n return `<h1 class=\"t\" style=\"margin:0 0 12px;font-family:${t.fontFamily};font-size:${size}px;line-height:1.3;font-weight:700;color:${t.textColor};\">${escapeHtml(text)}</h1>`;\n}\n\nexport function text(content: string, opts: { muted?: boolean; theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const color = opts.muted ? t.mutedColor : t.textColor;\n // Only primary body text carries `.t`; muted text keeps its lighter grey, which\n // already reads on a dark background (see the dark-mode @media rule in render()).\n const cls = opts.muted ? \"\" : ` class=\"t\"`;\n return `<p${cls} style=\"margin:0 0 16px;font-family:${t.fontFamily};font-size:15px;line-height:1.6;color:${color};\">${escapeHtml(content)}</p>`;\n}\n\n/** Raw HTML passthrough — you are responsible for escaping. */\nexport function html(raw: string): string {\n return raw;\n}\n\n/** A bulletproof, table-based CTA button that renders in Outlook too. */\nexport function button(\n label: string,\n href: string,\n opts: { theme?: Partial<Theme> } = {},\n): string {\n const t = theme(opts.theme);\n const safeHref = escapeHtml(href);\n return `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:8px 0 20px;\"><tr><td align=\"center\" bgcolor=\"${t.brandColor}\" style=\"border-radius:8px;\">\n<a href=\"${safeHref}\" target=\"_blank\" style=\"display:inline-block;padding:12px 28px;font-family:${t.fontFamily};font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:8px;background:${t.brandColor};\">${escapeHtml(label)}</a>\n</td></tr></table>`;\n}\n\n/** A large, letter-spaced code block — perfect for OTPs. */\nexport function code(value: string, opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n return `<div style=\"margin:0 0 20px;padding:16px;text-align:center;background:${t.bg};border:1px solid ${t.borderColor};border-radius:${t.borderRadius};font-family:'SFMono-Regular',Consolas,Menlo,monospace;font-size:32px;font-weight:700;letter-spacing:8px;color:${t.textColor};\">${escapeHtml(value)}</div>`;\n}\n\nexport function divider(opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n return `<hr style=\"border:none;border-top:1px solid ${t.borderColor};margin:24px 0;\" />`;\n}\n\nexport function spacer(height = 16): string {\n return `<div style=\"line-height:${height}px;height:${height}px;\"> </div>`;\n}\n\nexport function image(src: string, opts: { alt?: string; width?: number } = {}): string {\n const w = opts.width ? `width=\"${opts.width}\" style=\"max-width:100%;height:auto;display:block;margin:0 auto;\"` : `style=\"max-width:100%;height:auto;display:block;margin:0 auto;\"`;\n return `<img src=\"${escapeHtml(src)}\" alt=\"${escapeHtml(opts.alt ?? \"\")}\" ${w} />`;\n}\n\nexport function list(items: string[], opts: { theme?: Partial<Theme> } = {}): string {\n const t = theme(opts.theme);\n const lis = items\n .map(\n (i) =>\n `<li style=\"margin:0 0 8px;font-family:${t.fontFamily};font-size:15px;line-height:1.6;color:${t.textColor};\">${escapeHtml(i)}</li>`,\n )\n .join(\"\");\n return `<ul style=\"margin:0 0 16px;padding-left:20px;\">${lis}</ul>`;\n}\n\n/** A simple two-column key/value table — great for invoices & receipts. */\nexport function keyValue(\n rows: [string, string][],\n opts: { theme?: Partial<Theme> } = {},\n): string {\n const t = theme(opts.theme);\n const body = rows\n .map(\n ([k, v]) =>\n `<tr><td style=\"padding:8px 0;font-family:${t.fontFamily};font-size:14px;color:${t.mutedColor};\">${escapeHtml(k)}</td><td align=\"right\" style=\"padding:8px 0;font-family:${t.fontFamily};font-size:14px;font-weight:600;color:${t.textColor};\">${escapeHtml(v)}</td></tr>`,\n )\n .join(\"\");\n return `<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin:0 0 16px;border-top:1px solid ${t.borderColor};\">${body}</table>`;\n}\n\n/* ------------------------------------------------------------------ *\n * Layout\n * ------------------------------------------------------------------ */\n\nexport interface EmailOptions {\n title?: string;\n /** Hidden inbox-preview text. */\n preheader?: string;\n theme?: Partial<Theme>;\n /** Footer HTML/text (already trusted). */\n footer?: string;\n brandName?: string;\n logoUrl?: string;\n width?: number;\n}\n\n/** Wrap composed blocks in a responsive, email-client-safe document. */\nexport function render(opts: EmailOptions, blocks: string[]): string {\n const t = theme(opts.theme);\n const width = opts.width ?? 600;\n const preheader = opts.preheader\n ? `<div style=\"display:none;max-height:0;overflow:hidden;opacity:0;\">${escapeHtml(opts.preheader)}</div>`\n : \"\";\n const brand = opts.logoUrl\n ? `<img src=\"${escapeHtml(opts.logoUrl)}\" alt=\"${escapeHtml(opts.brandName ?? \"\")}\" height=\"32\" style=\"display:block;height:32px;\" />`\n : opts.brandName\n ? `<span style=\"font-family:${t.fontFamily};font-size:18px;font-weight:700;color:${t.brandColor};\">${escapeHtml(opts.brandName)}</span>`\n : \"\";\n const header = brand ? `<tr><td style=\"padding:0 0 20px;\">${brand}</td></tr>` : \"\";\n const footer = opts.footer\n ? `<tr><td style=\"padding:24px 8px 0;font-family:${t.fontFamily};font-size:12px;line-height:1.6;color:${t.mutedColor};text-align:center;\">${opts.footer}</td></tr>`\n : \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<meta name=\"x-apple-disable-message-reformatting\" />\n<title>${escapeHtml(opts.title ?? \"\")}</title>\n<style>\n @media only screen and (max-width:600px){ .container{width:100%!important;} .px{padding-left:20px!important;padding-right:20px!important;} }\n @media (prefers-color-scheme: dark){ body,.bg{background:#0b0b12!important;} .card{background:#16161f!important;} .t{color:#e5e7eb!important;} }\n</style>\n</head>\n<body class=\"bg\" style=\"margin:0;padding:0;background:${t.bg};\">\n${preheader}\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" class=\"bg\" style=\"background:${t.bg};padding:32px 0;\">\n<tr><td align=\"center\">\n<table role=\"presentation\" width=\"${width}\" class=\"container\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:${width}px;max-width:${width}px;\">\n${header}\n<tr><td class=\"card px\" style=\"background:${t.cardBg};border:1px solid ${t.borderColor};border-radius:${t.borderRadius};padding:32px;\">\n${blocks.join(\"\\n\")}\n</td></tr>\n${footer}\n</table>\n</td></tr>\n</table>\n</body>\n</html>`;\n}\n\n/* ------------------------------------------------------------------ *\n * Ready-made templates\n * ------------------------------------------------------------------ */\n\nexport interface BrandInfo {\n brandName?: string;\n logoUrl?: string;\n theme?: Partial<Theme>;\n footer?: string;\n}\n\n/** One-time-passcode email. */\nexport function otpEmail(\n o: { code: string; heading?: string; message?: string; expiresMinutes?: number } & BrandInfo,\n): string {\n return render(\n { title: o.heading ?? \"Your verification code\", preheader: `Your code is ${o.code}`, ...o },\n [\n heading(o.heading ?? \"Verify your email\", { theme: o.theme }),\n text(o.message ?? \"Use the code below to continue. Never share it with anyone.\", {\n theme: o.theme,\n }),\n code(o.code, { theme: o.theme }),\n text(\n `This code expires in ${o.expiresMinutes ?? 10} minutes.`,\n { muted: true, theme: o.theme },\n ),\n ],\n );\n}\n\n/** Welcome / onboarding email with a CTA. */\nexport function welcomeEmail(\n o: { name?: string; message?: string; ctaLabel?: string; ctaHref?: string } & BrandInfo,\n): string {\n const blocks = [\n heading(o.name ? `Welcome, ${o.name}!` : \"Welcome aboard!\", { theme: o.theme }),\n text(o.message ?? \"We're thrilled to have you. Let's get you started.\", { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"Get started\", o.ctaHref, { theme: o.theme }));\n return render({ title: \"Welcome\", preheader: o.message, ...o }, blocks);\n}\n\n/** Alert / notification email. */\nexport function alertEmail(\n o: { title: string; message: string; ctaLabel?: string; ctaHref?: string } & BrandInfo,\n): string {\n const blocks = [\n heading(o.title, { theme: o.theme }),\n text(o.message, { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"View details\", o.ctaHref, { theme: o.theme }));\n return render({ preheader: o.message, ...o }, blocks);\n}\n\n/** Invoice / receipt email. */\nexport function invoiceEmail(\n o: {\n heading?: string;\n intro?: string;\n rows: [string, string][];\n total?: [string, string];\n ctaLabel?: string;\n ctaHref?: string;\n } & BrandInfo,\n): string {\n const blocks = [\n heading(o.heading ?? \"Payment receipt\", { theme: o.theme }),\n text(o.intro ?? \"Thanks for your payment. Here's your receipt.\", { theme: o.theme }),\n keyValue(o.total ? [...o.rows, o.total] : o.rows, { theme: o.theme }),\n ];\n if (o.ctaHref) blocks.push(button(o.ctaLabel ?? \"View invoice\", o.ctaHref, { theme: o.theme }));\n return render({ title: o.heading ?? \"Receipt\", ...o }, blocks);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lacspace/email-templates",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Compose bulletproof, responsive, dark-mode-aware HTML emails from simple blocks — buttons, OTP codes, invoices — with ready-made OTP/welcome/alert/invoice templates. Zero-dependency, isomorphic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
],
|
|
46
46
|
"author": "Lacspace <contact@lacspace.com>",
|
|
47
47
|
"license": "SEE LICENSE IN LICENSE",
|
|
48
|
-
"homepage": "https://lacspace.com/packages",
|
|
48
|
+
"homepage": "https://developer.lacspace.com/packages/email-templates",
|
|
49
49
|
"repository": {
|
|
50
50
|
"type": "git",
|
|
51
51
|
"url": "git+https://github.com/lacspace/npm-packages.git",
|