{"_id":"@cartella/renderer-email","_rev":"4-b829a64fab4c824783ed8fd2f16262a7","name":"@cartella/renderer-email","dist-tags":{"latest":"0.5.0"},"versions":{"0.2.0":{"name":"@cartella/renderer-email","version":"0.2.0","license":"MIT","_id":"@cartella/renderer-email@0.2.0","maintainers":[{"name":"nyingmeh","email":"nyingmeh@gmail.com"}],"homepage":"https://github.com/bentosplate/cartella#readme","bugs":{"url":"https://github.com/bentosplate/cartella/issues"},"dist":{"shasum":"be1c69a2b4f218e52f1761816fb44695f02619b3","tarball":"https://registry.npmjs.org/@cartella/renderer-email/-/renderer-email-0.2.0.tgz","fileCount":7,"integrity":"sha512-tNDRZv7SoNs+lmaGJVjT2giUTSy3zmKyaAoe4EPUnUVdHnjmEcl9yaqa/bODTxZFN8/SiJ+EjGPGcvoxxa/YWg==","signatures":[{"sig":"MEYCIQCACMeG8SWB62uq7NelUaif6GhRoYtaRdr/N/VABx+gagIhAP+KkTlg89n/pYz540EcK9azJsRzGpL8CqP/wiLnyrFy","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":220941},"main":"./dist/index.js","type":"module","_from":"file:cartella-renderer-email-0.2.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"eslint src tests","size":"size-limit","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"nyingmeh","email":"nyingmeh@gmail.com"},"_resolved":"C:\\Users\\nying\\AppData\\Local\\Temp\\f15e3d67781558c6208c0162926b0775\\cartella-renderer-email-0.2.0.tgz","_integrity":"sha512-tNDRZv7SoNs+lmaGJVjT2giUTSy3zmKyaAoe4EPUnUVdHnjmEcl9yaqa/bODTxZFN8/SiJ+EjGPGcvoxxa/YWg==","repository":{"url":"git+https://github.com/bentosplate/cartella.git","type":"git","directory":"packages/renderer-email"},"_npmVersion":"10.9.2","description":"Canvas JSON → cross-client-safe HTML email. Outlook-VML / Apple Mail / Gmail / iOS / Android.","directories":{},"sideEffects":false,"_nodeVersion":"22.16.0","dependencies":{"@cartella/core":"^1.6.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/renderer-email_0.2.0_1783813021975_0.9991794008848955","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@cartella/renderer-email","version":"0.3.0","license":"MIT","_id":"@cartella/renderer-email@0.3.0","maintainers":[{"name":"nyingmeh","email":"nyingmeh@gmail.com"}],"homepage":"https://github.com/bentosplate/cartella#readme","bugs":{"url":"https://github.com/bentosplate/cartella/issues"},"dist":{"shasum":"860993af593f4af03367aebd00e17532dcab8545","tarball":"https://registry.npmjs.org/@cartella/renderer-email/-/renderer-email-0.3.0.tgz","fileCount":7,"integrity":"sha512-qfV0FQx2+RqJAgz1dTXxo1CiEi70J+Wl0j4FICcLPB/IjLmWi/XGecpzroaFLtWgDV3eJ/J1v3BZkAotFm2O6w==","signatures":[{"sig":"MEUCIQDgcKHdkP7AQEdJxxxLSDUygOGGGQTV/f5fqllWhPY4GgIgVoHpZTGzIxrTfPI3/TBx4c1Z7wOQ4gjlJhlX6oe+TzI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":239015},"main":"./dist/index.js","type":"module","_from":"file:cartella-renderer-email-0.3.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"eslint src tests","size":"size-limit","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"nyingmeh","email":"nyingmeh@gmail.com"},"_resolved":"/tmp/7cbd5cfe082f77e0b42b16e90cc3411e/cartella-renderer-email-0.3.0.tgz","_integrity":"sha512-qfV0FQx2+RqJAgz1dTXxo1CiEi70J+Wl0j4FICcLPB/IjLmWi/XGecpzroaFLtWgDV3eJ/J1v3BZkAotFm2O6w==","repository":{"url":"git+https://github.com/bentosplate/cartella.git","type":"git","directory":"packages/renderer-email"},"_npmVersion":"10.8.2","description":"Canvas JSON → cross-client-safe HTML email. Outlook-VML / Apple Mail / Gmail / iOS / Android.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","dependencies":{"@cartella/core":"^1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/renderer-email_0.3.0_1783925823818_0.8523484859384978","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@cartella/renderer-email","version":"0.4.0","license":"MIT","_id":"@cartella/renderer-email@0.4.0","maintainers":[{"name":"nyingmeh","email":"nyingmeh@gmail.com"}],"homepage":"https://github.com/bentosplate/cartella#readme","bugs":{"url":"https://github.com/bentosplate/cartella/issues"},"dist":{"shasum":"07b04114fe4d36617fadec7bdd32615d08ba7046","tarball":"https://registry.npmjs.org/@cartella/renderer-email/-/renderer-email-0.4.0.tgz","fileCount":7,"integrity":"sha512-M8XaS5B1i0/r5IgqILFSV218ol4IXy6zfDgHxFbLESbbxP2opfvuadnNevS0qyZzrtfKaD/bY++h0J4pqKfulg==","signatures":[{"sig":"MEQCIHqopryf8VaKW81YFQ11YsiOJ1ADUPhVxBxyhyRWnCv2AiApLLCpbADsb7Dgr4YQZVLeE9M1DzyDgzulz9KA8SLdEg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":263065},"main":"./dist/index.js","type":"module","_from":"file:cartella-renderer-email-0.4.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"eslint src tests","size":"size-limit","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"nyingmeh","email":"nyingmeh@gmail.com"},"_resolved":"/tmp/aded5359f7c1d5ad54bed08c37527293/cartella-renderer-email-0.4.0.tgz","_integrity":"sha512-M8XaS5B1i0/r5IgqILFSV218ol4IXy6zfDgHxFbLESbbxP2opfvuadnNevS0qyZzrtfKaD/bY++h0J4pqKfulg==","repository":{"url":"git+https://github.com/bentosplate/cartella.git","type":"git","directory":"packages/renderer-email"},"_npmVersion":"10.8.2","description":"Canvas JSON → cross-client-safe HTML email. Outlook-VML / Apple Mail / Gmail / iOS / Android.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","dependencies":{"@cartella/core":"^1.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/renderer-email_0.4.0_1784159863291_0.3567569536887212","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@cartella/renderer-email","version":"0.5.0","description":"Canvas JSON → cross-client-safe HTML email. Outlook-VML / Apple Mail / Gmail / iOS / Android.","license":"MIT","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"sideEffects":false,"dependencies":{"@cartella/core":"^1.9.0"},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/bentosplate/cartella.git","directory":"packages/renderer-email"},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","lint":"eslint src tests","size":"size-limit","clean":"rm -rf dist .turbo"},"_id":"@cartella/renderer-email@0.5.0","bugs":{"url":"https://github.com/bentosplate/cartella/issues"},"homepage":"https://github.com/bentosplate/cartella#readme","_integrity":"sha512-Xvd+Tzn4guvLOPzpOTJhwyX7c9Ru70v0I+xxOZxad/Tz8Yy4mO1VnZ6y/fcIk4ftOVc0yhGTwvxvLAZlWkh6hg==","_resolved":"/tmp/f8e3703acfa64cda82175f085cdc0e20/cartella-renderer-email-0.5.0.tgz","_from":"file:cartella-renderer-email-0.5.0.tgz","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-Xvd+Tzn4guvLOPzpOTJhwyX7c9Ru70v0I+xxOZxad/Tz8Yy4mO1VnZ6y/fcIk4ftOVc0yhGTwvxvLAZlWkh6hg==","shasum":"2b8bd0b678ba6d630a86a419332182d080676795","tarball":"https://registry.npmjs.org/@cartella/renderer-email/-/renderer-email-0.5.0.tgz","fileCount":7,"unpackedSize":266073,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDn7DYCuYBSKKxgbm3kE5s2qlFvEN5C6pFM1yXhUHpFQwIgLR8D+8JdKdk2JyB62lHwPp67VR1IAs7M3Nn7p6AL0SI="}]},"_npmUser":{"name":"nyingmeh","email":"nyingmeh@gmail.com"},"directories":{},"maintainers":[{"name":"nyingmeh","email":"nyingmeh@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/renderer-email_0.5.0_1784551716112_0.9139302472421533"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-11T23:37:01.754Z","modified":"2026-07-20T12:48:36.625Z","0.2.0":"2026-07-11T23:37:02.132Z","0.3.0":"2026-07-13T06:57:03.968Z","0.4.0":"2026-07-15T23:57:43.475Z","0.5.0":"2026-07-20T12:48:36.268Z"},"bugs":{"url":"https://github.com/bentosplate/cartella/issues"},"license":"MIT","homepage":"https://github.com/bentosplate/cartella#readme","repository":{"type":"git","url":"git+https://github.com/bentosplate/cartella.git","directory":"packages/renderer-email"},"description":"Canvas JSON → cross-client-safe HTML email. Outlook-VML / Apple Mail / Gmail / iOS / Android.","maintainers":[{"name":"nyingmeh","email":"nyingmeh@gmail.com"}],"readme":"# @cartella/renderer-email\n\nCanvas JSON → cross-client-safe HTML email.\n\nCompiles an `email`-doctype Cartella canvas into a standalone HTML email\ndocument. Nested `<table>` layouts, inline styles, MSO conditional\ncomments for Outlook fallbacks, bulletproof VML buttons, and a single\n`@media (max-width: 480px)` block for mobile stacking. Zero deps beyond\n`@cartella/core`. Isomorphic — runs in Node and the browser.\n\n> **Status:** phase 2b — library-only. The Studio editor (`@cartella/studio`)\n> for designing email canvases lands in phase 2d. For now, hand-author\n> the JSON or use the reducer.\n\n## Install\n\n```sh\npnpm add @cartella/renderer-email @cartella/core\n```\n\n## Use\n\n```ts\nimport { renderEmail } from '@cartella/renderer-email';\n\nconst { html, attachments } = renderEmail(canvas, {\n  subject: 'Your certificate is ready',\n  preheader: 'Click below to download — no expiry.',\n  data: {\n    studentName: 'Amara',\n    certificate: { url: 'https://...', imageUrl: 'https://...' },\n  },\n});\n\n// `html` is a self-contained email document. Hand it to your mailer.\n// `attachments` lists any `email-document-ref` blocks with\n// deliveryMode='attachment'; your mailer fetches the PDF and attaches it.\n```\n\n## What the renderer guarantees\n\n- **XHTML 1.0 Transitional doctype** — Outlook desktop's Word renderer\n  requires it.\n- **All styling inline.** Gmail strips `<style>` blocks under \"Auto-show\n  inline images\"; we don't trust the document stylesheet for content.\n- **Bulletproof buttons.** Outlook gets a `<v:roundrect>` VML branch\n  inside `<!--[if mso]>`; everyone else gets a styled `<a>`. Same\n  visual button across the matrix.\n- **Mobile stacking** via `cartella-column.mobile-stack` and an\n  `@media (max-width: 480px)` block. Opt-out per column with\n  `mobileStack: false`.\n- **Dark-mode opt-in.** `<meta name=\"color-scheme\" content=\"light dark\">`\n  plus a `prefers-color-scheme: dark` block and Apple Mail / Outlook web\n  vendor selectors. Authors don't design for dark explicitly — clients\n  auto-invert.\n- **Safe URLs.** `javascript:` / `vbscript:` / non-image `data:` schemes\n  rewrite to `#`. Defence-in-depth: substitution runs before sanitisation,\n  so a merge value that resolves to a dangerous URL is still caught.\n\n## Client coverage targets\n\nApple Mail (macOS + iOS), Gmail (web / iOS / Android), Outlook 365 web,\n**Outlook desktop**, Yahoo, ProtonMail. Pre-2018 clients (Outlook\n2007–2013, Lotus Notes, AOL desktop) are explicitly out of scope.\n\nCross-client _visual_ snapshot testing (Litmus / Email on Acid) is\nphase 2c. The 2b snapshots in `tests/snapshots/` are HTML-string diffs\nonly — they catch markup regressions, not rendering differences between\nclients.\n\n## Variable substitution\n\n`{{var}}` and `{{var||fallback}}` syntax — a tiny subset of Mustache,\nmatching the engine Academia already uses on their email-template side:\n\n| Template                | `data`                      | Output           |\n| ----------------------- | --------------------------- | ---------------- |\n| `Hi {{name}}`           | `{ name: 'Ada' }`           | `Hi Ada`         |\n| `Hi {{name}}`           | `{}`                        | `Hi `            |\n| `Hi {{name\\|\\|Friend}}` | `{}`                        | `Hi Friend`      |\n| `{{a.b.c}}`             | `{ a: { b: { c: 'deep' }}}` | `deep`           |\n| `{{n}}`                 | `{ n: 0 }`                  | `0`              |\n| `{{user}}`              | `{ user: { name: 'Ada' } }` | (empty — object) |\n| `{{x}}`                 | `{ x: '<script>' }`         | `&lt;script&gt;` |\n\nSubstitution applies to **text content**, **image src / alt / href**,\n**button label / href**, and the implicit `{{<refField>.url}}` /\n`{{<refField>.imageUrl}}` constructed by `email-document-ref`. Output\nis HTML-escaped per context after substitution — merge values flow\nthrough the same defence as static authoring.\n\n## `email-document-ref` delivery modes\n\nReference a Cartella-designed document (certificate, ID card, receipt)\nfrom the email body. Each mode compiles to a different markup shape:\n\n| `deliveryMode` | Compiles to                                                                                           |\n| -------------- | ----------------------------------------------------------------------------------------------------- |\n| `link`         | Bulletproof button. Href = `{{<refField>.url}}`.                                                      |\n| `image`        | `<img>` preview. Src = `{{<refField>.imageUrl}}`. Alt falls back to `templateName`.                   |\n| `both`         | Image preview followed by the bulletproof button.                                                     |\n| `attachment`   | No body markup. Pushes `{templateId, refField}` onto the returned `attachments` array. Host attaches. |\n\n`refField` defaults to `'document'`. The host wires per-recipient data\nthrough it: Academia's certificate flow uses `refField: 'certificate'`\nand passes `{ certificate: { url, imageUrl, alt } }`.\n\n## Examples\n\nHand-authored email canvases live in [`tests/fixtures/`](./tests/fixtures/):\n\n- `welcome.json` — single section, text + button, basic merges.\n- `receipt.json` — divider stack, multi-section, line items.\n- `newsletter.json` — hero image, 2-col feature row.\n- `appeal-decision.json` — Academia letterhead with Georgia serif.\n- `certificate-delivery.json` — `email-document-ref` `both`.\n- `payment-receipt.json` — `email-document-ref` `attachment`.\n- `transactional-acceptance.json` — dark hero + light body.\n- `mobile-stacking.json` — 4×25% columns to exercise mobile stack.\n\nTheir pinned outputs in [`tests/snapshots/`](./tests/snapshots/) are\nthe easiest way to see what real renderer output looks like.\n\n## See also\n\n- [`@cartella/core`](../core) — the Canvas schema this renderer consumes.\n- [`docs/email-mode-plan.md`](../../docs/email-mode-plan.md) — the multi-phase work plan this package implements.\n","readmeFilename":"README.md"}