@lupinum/nuxt-email 1.0.0-beta.3 → 1.0.0-beta.5

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +49 -17
  3. package/THIRD_PARTY_NOTICES +4 -4
  4. package/dist/agent/AGENTS.md +43 -0
  5. package/dist/agent/pages/docs/components/code.md +68 -0
  6. package/dist/agent/pages/docs/components/content.md +111 -0
  7. package/dist/agent/pages/docs/components/layout.md +89 -0
  8. package/dist/agent/pages/docs/components/markdown.md +49 -0
  9. package/dist/agent/pages/docs/components/structure.md +122 -0
  10. package/dist/agent/pages/docs/components/tailwind.md +41 -0
  11. package/dist/agent/pages/docs/components.md +56 -0
  12. package/dist/agent/pages/docs/getting-started/choose-a-tool.md +38 -0
  13. package/dist/agent/pages/docs/getting-started/first-email.md +99 -0
  14. package/dist/agent/pages/docs/getting-started/installation.md +85 -0
  15. package/dist/agent/pages/docs/getting-started/render-from-nitro.md +59 -0
  16. package/dist/agent/pages/docs/getting-started.md +64 -0
  17. package/dist/agent/pages/docs/guides/client-compatibility.md +65 -0
  18. package/dist/agent/pages/docs/guides/plain-text.md +42 -0
  19. package/dist/agent/pages/docs/guides/preview-workflow.md +83 -0
  20. package/dist/agent/pages/docs/guides/sending-email.md +132 -0
  21. package/dist/agent/pages/docs/guides/standalone-rendering.md +118 -0
  22. package/dist/agent/pages/docs/guides/subject-lines.md +95 -0
  23. package/dist/agent/pages/docs/guides/tailwind.md +128 -0
  24. package/dist/agent/pages/docs/guides/testing-your-emails.md +118 -0
  25. package/dist/agent/pages/docs/guides.md +32 -0
  26. package/dist/agent/pages/docs/reference/conformance.md +55 -0
  27. package/dist/agent/pages/docs/reference/define-email.md +69 -0
  28. package/dist/agent/pages/docs/reference/errors.md +72 -0
  29. package/dist/agent/pages/docs/reference/migration.md +144 -0
  30. package/dist/agent/pages/docs/reference/module.md +90 -0
  31. package/dist/agent/pages/docs/reference/render-email.md +107 -0
  32. package/dist/agent/pages/docs/reference.md +30 -0
  33. package/dist/module.json +1 -1
  34. package/dist/runtime/components/EButton.d.ts +1 -1
  35. package/dist/runtime/tailwind/engine/css/constants.d.ts +1 -0
  36. package/dist/runtime/tailwind/engine/css/constants.js +1 -0
  37. package/dist/runtime/tailwind/engine/css/downlevel-for-email-clients.js +2 -1
  38. package/dist/runtime/tailwind/engine/css/extract-rules-per-class.js +91 -33
  39. package/dist/runtime/tailwind/engine/css/sanitize-non-inlinable-rules.js +7 -2
  40. package/dist/runtime/tailwind/engine/stylesheets.d.ts +3 -3
  41. package/dist/runtime/tailwind/engine/stylesheets.js +141 -34
  42. package/package.json +23 -28
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0-beta.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#63](https://github.com/lupinum-dev/nuxt-email/pull/63) [`8ceae4a`](https://github.com/lupinum-dev/nuxt-email/commit/8ceae4a7cf9439962e03f01ceb50bffa0b8fdd0f) Thanks [@Mat4m0](https://github.com/Mat4m0)! - Fix conditional and child utility styles with the current Tailwind compiler, and fail instead of dropping unsupported conditions.
8
+
9
+ `ETailwind` keeps media-query, pseudo-class, child (`*:`, `**:`) and arbitrary selector variants such as `[:where(&)]:` with their conditions. Supported wrappers around a utility are `@media` and `@supports` (`@layer` is ignored). A utility inside any other at-rule now throws, for example `@container` variants (`@sm:`) or custom CSS in the `ETailwind` `utility` prop that wraps a class in `@scope` or `@container`. It throws `Unable to render Tailwind CSS: @<name> rules are not supported.` Before, the condition was dropped silently and the style applied everywhere.
10
+
11
+ Migration: remove `@container` variants (`@sm:`, `@md:` and so on) from email templates, and move custom CSS in the `ETailwind` `utility` prop out of `@container`, `@scope` and other unsupported at-rules, or rewrite the condition with `@media` or `@supports`.
12
+
13
+ ## v1.0.0-beta.4
14
+
15
+ [compare changes](https://github.com/lupinum-dev/nuxt-email/compare/v1.0.0-beta.3...v1.0.0-beta.4)
16
+
17
+ ### 🚀 Enhancements
18
+
19
+ - **docs:** Ship versioned package guidance ([#55](https://github.com/lupinum-dev/nuxt-email/pull/55))
20
+
21
+ ### 🩹 Fixes
22
+
23
+ - **deps:** Clear workspace advisories and update Vitest ([#54](https://github.com/lupinum-dev/nuxt-email/pull/54))
24
+
25
+ ### ❤️ Contributors
26
+
27
+ - Matthias Amon
28
+
3
29
  ## v1.0.0-beta.3
4
30
 
5
31
  [compare changes](https://github.com/lupinum-dev/nuxt-email/compare/v1.0.0-beta.2...v1.0.0-beta.3)
package/README.md CHANGED
@@ -20,12 +20,12 @@
20
20
  <a href="https://www.npmjs.com/package/@lupinum/nuxt-email"><img src="https://img.shields.io/npm/v/@lupinum/nuxt-email?color=00DC82" alt="npm version"></a>
21
21
  <a href="https://github.com/lupinum-dev/nuxt-email/actions/workflows/ci.yml"><img src="https://github.com/lupinum-dev/nuxt-email/actions/workflows/ci.yml/badge.svg" alt="CI status"></a>
22
22
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-00DC82" alt="MIT license"></a>
23
- <a href="https://discord.gg/RPH6SeA36N"><img src="https://img.shields.io/badge/Discord-18181B?logo=discord" alt="Discord"></a>
23
+ <a href="https://discord.lupinum.com"><img src="https://img.shields.io/badge/Discord-18181B?logo=discord" alt="Discord"></a>
24
24
  <a href="https://deepwiki.com/lupinum-dev/nuxt-email"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
25
25
  </p>
26
26
 
27
27
  > [!WARNING]
28
- > [`1.0.0-beta.2`](https://www.npmjs.com/package/@lupinum/nuxt-email/v/1.0.0-beta.2) is published on npm's `next` tag. The [three-client beta smoke test](https://github.com/lupinum-dev/nuxt-email/blob/main/docs/release/client-qa-checklist.md#100-beta1-smoke-test) passed against beta.1; the full eight-client checklist and an external transactional beta still block stable `1.0.0`. The unscoped `nuxt-email` package on npm is unrelated to this project.
28
+ > The 1.0.0 beta is published on npm's [`next` tag](https://www.npmjs.com/package/@lupinum/nuxt-email?activeTab=versions). The [three-client beta smoke test](https://github.com/lupinum-dev/nuxt-email/blob/main/docs/release/client-qa-checklist.md#100-beta1-smoke-test) passed against beta.1; the full eight-client checklist and an external transactional beta still block stable `1.0.0`. The unscoped `nuxt-email` package on npm is unrelated to this project.
29
29
 
30
30
  ## Why use Nuxt Email?
31
31
 
@@ -113,20 +113,52 @@ export default defineEventHandler(async () => {
113
113
 
114
114
  The template name and props are generated from the Vue file. Invalid names and props fail during type checking. Use a sibling fixture to inspect it in `/__email`, then [pass the rendered result to your provider](https://nuxt-email.lupinum.com/docs/guides/sending-email).
115
115
 
116
- ## Discord
116
+ Questions? Ask in the [Lupinum OSS Discord](https://discord.lupinum.com).
117
117
 
118
- Join the Lupinum OSS community to discuss Nuxt Email, ask questions, and share
119
- what you build.
118
+ ## Agent setup
120
119
 
121
- <p align="center">
122
- <a href="https://discord.gg/RPH6SeA36N">
123
- <picture>
124
- <source media="(prefers-color-scheme: dark)" srcset="docs/public/discord-dark.svg">
125
- <source media="(prefers-color-scheme: light)" srcset="docs/public/discord-light.svg">
126
- <img src="docs/public/discord-light.svg" width="500" alt="Join the Lupinum OSS Discord">
127
- </picture>
128
- </a>
129
- </p>
120
+ `@lupinum/nuxt-email` ships its documentation inside the package for coding
121
+ agents such as Claude Code, Codex and Cursor. The pages match the installed
122
+ version, so an agent does not follow the website for a different version.
123
+
124
+ To set it up, give your coding agent this task:
125
+
126
+ ```text
127
+ Set up @lupinum/nuxt-email for coding agents as described under "Agent setup" in its README.
128
+ ```
129
+
130
+ ### Steps for the agent
131
+
132
+ 1. Find `node_modules/@lupinum/nuxt-email/dist/agent/AGENTS.md`. Look in the
133
+ directory of the project that depends on `@lupinum/nuxt-email` first, then
134
+ in its parent directories up to the repository root (workspaces can hoist
135
+ packages). Read it; it lists the documentation pages.
136
+ 2. Add the section below to the project's agent instructions: `AGENTS.md`, or
137
+ `CLAUDE.md` if the project has only that file. If it has neither, create
138
+ `AGENTS.md`. Write the path relative to the repository root, through
139
+ `node_modules/@lupinum/nuxt-email` (for example
140
+ `apps/web/node_modules/@lupinum/nuxt-email/...` in a workspace). Never write
141
+ a resolved path such as `node_modules/.pnpm/...`: it contains the version and
142
+ breaks after an upgrade. If a section for `@lupinum/nuxt-email` already
143
+ exists, leave it as it is.
144
+
145
+ Use the path you found in place of the sample path:
146
+
147
+ ```md
148
+ ## @lupinum/nuxt-email
149
+
150
+ Before you change code that uses @lupinum/nuxt-email, read
151
+ `node_modules/@lupinum/nuxt-email/dist/agent/AGENTS.md` and the pages it
152
+ lists. They document the installed version. Prefer them over what you
153
+ remember about this package and over the website.
154
+ ```
155
+
156
+ 3. Do not copy the documentation into the project and do not install a skill.
157
+ The section points into the installed package, so it stays correct after
158
+ every upgrade or downgrade.
159
+
160
+ If the file does not exist, the installed version has no packaged
161
+ documentation. Read the package README and its TypeScript types instead.
130
162
 
131
163
  ## Email components
132
164
 
@@ -196,7 +228,7 @@ See the [changelog](CHANGELOG.md) for released changes.
196
228
 
197
229
  ## Contributing and development
198
230
 
199
- Read the [contribution guide](https://github.com/lupinum-dev/nuxt-email/blob/main/CONTRIBUTING.md) before you open a pull request. Run the normal handoff gate before you submit a change:
231
+ Read the [contribution guide](https://github.com/lupinum-dev/nuxt-email/blob/main/.github/CONTRIBUTING.md) before you open a pull request. Run the normal handoff gate before you submit a change:
200
232
 
201
233
  ```bash
202
234
  corepack enable
@@ -204,11 +236,11 @@ pnpm install --frozen-lockfile
204
236
  pnpm verify
205
237
  ```
206
238
 
207
- Maintainers use the protected workflow in [MAINTAINING.md](https://github.com/lupinum-dev/nuxt-email/blob/main/MAINTAINING.md) for releases.
239
+ Maintainers use the protected workflow in [Lupinum OSS](https://oss.lupinum.com/docs/releasing) for releases.
208
240
 
209
241
  ## Support and security
210
242
 
211
- Open a [GitHub issue](https://github.com/lupinum-dev/nuxt-email/issues) for bugs and focused proposals. Join the [Lupinum OSS Discord](https://discord.gg/RPH6SeA36N) for project discussion.
243
+ Open a [GitHub issue](https://github.com/lupinum-dev/nuxt-email/issues) for bugs and focused proposals. Join the [Lupinum OSS Discord](https://discord.lupinum.com) for project discussion.
212
244
 
213
245
  Use the [private security process](https://github.com/lupinum-dev/nuxt-email/security/policy) to report a vulnerability. Do not report a vulnerability in a public issue.
214
246
 
@@ -2,12 +2,12 @@ React Email
2
2
  ===========
3
3
 
4
4
  Nuxt Email contains Vue adaptations of selected compatibility algorithms and
5
- markup strategies from React Email 6.9.0, pinned at source commit
6
- 6eb428924c4c2774228a07cbec1977ad8898f143. The adapted surface covers button
5
+ markup strategies from React Email 6.11.0, pinned at source commit
6
+ 15419ff1f4cd0e32ed2c15a4a9182cc47a200a60. The adapted surface covers button
7
7
  padding and Outlook spacing, text margin expansion, table padding placement,
8
8
  hidden preview text, the font-face declaration, inline code rendering, Markdown
9
9
  rendering, and Tailwind class inlining. The pinned upstream source is available at
10
- https://github.com/resend/react-email/tree/6eb428924c4c2774228a07cbec1977ad8898f143.
10
+ https://github.com/resend/react-email/tree/15419ff1f4cd0e32ed2c15a4a9182cc47a200a60.
11
11
 
12
12
  Copyright 2024 Plus Five Five, Inc
13
13
 
@@ -44,7 +44,7 @@ whose text is reproduced once below.
44
44
  - css-tree 3.2.1 — CSS parsing and generation in the Tailwind inlining engine.
45
45
  Copyright (C) 2016-2026 by Roman Dvornov
46
46
 
47
- - tailwindcss 4.1.18 — utility compilation used by ETailwind; its stylesheet CSS
47
+ - tailwindcss 4.3.3 — utility compilation used by ETailwind; its stylesheet CSS
48
48
  is also vendored byte-for-byte into the engine.
49
49
  Copyright (c) Tailwind Labs, Inc.
50
50
 
@@ -0,0 +1,43 @@
1
+ # @lupinum/nuxt-email 1.0.0-beta.5 documentation
2
+
3
+ These pages document the installed version 1.0.0-beta.5. Prefer them over what you
4
+ remember about this package and over the website, which may describe another version.
5
+ Read the pages that match the task before you write code. If they do not answer a question,
6
+ the user can ask in the Lupinum OSS Discord: https://discord.lupinum.com
7
+
8
+ > Typed transactional email for Nuxt, with email-safe Tailwind v4.
9
+
10
+ ## When to use
11
+
12
+ Use this site to author and render transactional email with Nuxt Email.
13
+
14
+ ## Documentation
15
+
16
+ - [Introduction](./pages/docs/getting-started.md): What Nuxt Email is, why its output is trustworthy, and how it is compared with a fixed React Email version.
17
+ - [Choose the right tool](./pages/docs/getting-started/choose-a-tool.md): Compare Nuxt Email with the closest Vue, Nuxt, standalone, and React email workflows.
18
+ - [Installation](./pages/docs/getting-started/installation.md): Add @lupinum/nuxt-email to a supported Nuxt application and optionally configure syntax highlighting.
19
+ - [Your first email](./pages/docs/getting-started/first-email.md): Write a Vue email template under app/emails/, add a typed preview fixture, and understand template naming.
20
+ - [Render from Nitro](./pages/docs/getting-started/render-from-nitro.md): Call the typed, server-only renderEmail auto-import and pass its html and text to your provider SDK.
21
+ - [Overview](./pages/docs/components.md): The built-in E-prefixed components, optional ECodeBlock, and their shared safety rules.
22
+ - [Structure](./pages/docs/components/structure.md): EHtml, EHead, EBody, EPreview, and EFont — the document shell, head, body, inbox preview text, and font declarations.
23
+ - [Layout](./pages/docs/components/layout.md): EContainer, ESection, ERow, and EColumn — presentation-table primitives for stable email layouts.
24
+ - [Content](./pages/docs/components/content.md): EText, EHeading, ELink, EImg, EHr, and EButton for visible email content.
25
+ - [Code](./pages/docs/components/code.md): Compatibility-safe inline code and opt-in syntax-highlighted blocks.
26
+ - [Markdown](./pages/docs/components/markdown.md): EMarkdown renders Markdown to email-safe HTML with inline styles and per-element overrides.
27
+ - [Tailwind](./pages/docs/components/tailwind.md): ETailwind compiles Tailwind v4 utilities into email-safe inline styles and head rules during server rendering.
28
+ - [Guides](./pages/docs/guides.md): Task-focused guides for authoring, previewing, testing, and sending transactional email.
29
+ - [Preview workflow](./pages/docs/guides/preview-workflow.md): Use the development-only /__email preview, add fixtures, and monitor exact rendered size without treating it as a client guarantee.
30
+ - [Sending email](./pages/docs/guides/sending-email.md): Hand Nuxt Email output to a provider SDK or a local Mailpit SMTP server.
31
+ - [Using Tailwind](./pages/docs/guides/tailwind.md): How ETailwind inlines utilities, how nested-component classes are reached, and the exact documented limitations.
32
+ - [Subject lines](./pages/docs/guides/subject-lines.md): Compute a subject line from the same typed props with defineEmail, and read it back from the render result.
33
+ - [Plain text](./pages/docs/guides/plain-text.md): How the plain-text fallback is produced from the rendered HTML, what it excludes, and how to control table output.
34
+ - [Testing your emails](./pages/docs/guides/testing-your-emails.md): Render templates and assert on their output in unit tests with the @lupinum/nuxt-email/testing utilities.
35
+ - [Client compatibility and proofs](./pages/docs/guides/client-compatibility.md): What is automatically proven, and the manual email-client QA checklist that closes the remaining gap.
36
+ - [Render outside Nitro](./pages/docs/guides/standalone-rendering.md): Compile the same Vue templates for a Node backend without booting Nuxt.
37
+ - [Reference](./pages/docs/reference.md): The precise contracts — renderEmail, module behavior, error types, conformance, and migration.
38
+ - [renderEmail](./pages/docs/reference/render-email.md): The one public render API — its signature, deterministic result shape, plain-text contract, and security boundary.
39
+ - [defineEmail](./pages/docs/reference/define-email.md): Declare a template subject or authored plain-text alternative from script setup.
40
+ - [Module behavior](./pages/docs/reference/module.md): What the module registers, its fixed conventions, and the opt-in codeBlock configuration.
41
+ - [Error types](./pages/docs/reference/errors.md): Public error classes, their stable fields, common symptoms, and recovery steps.
42
+ - [Conformance and divergences](./pages/docs/reference/conformance.md): How compatibility is measured against React Email, the current pass numbers, and every intentional divergence.
43
+ - [Migration from React Email](./pages/docs/reference/migration.md): Translate JSX templates, props, and rendering calls to Nuxt Email's Vue single-file components and generated server API.
@@ -0,0 +1,68 @@
1
+ ---
2
+ title: "Code"
3
+ description: "Compatibility-safe inline code and opt-in syntax-highlighted blocks."
4
+ url: "https://nuxt-email.lupinum.com/docs/components/code"
5
+ route: "/docs/components/code"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Code
13
+
14
+ > Compatibility-safe inline code and opt-in syntax-highlighted blocks.
15
+
16
+ ## ECodeInline
17
+
18
+ `ECodeInline` renders inline code inside a text flow. Place it inside `EText` (or another text element). It accepts safe HTML attributes and forwards them, plus `style`, to both emitted elements.
19
+
20
+ | Prop | Type | Default | Required |
21
+ | --- | --- | --- | --- |
22
+ | — | No component-specific props; accepts [shared safe native attributes](/raw/docs/components.md#rules-every-component-shares). | — | — |
23
+
24
+ It renders three siblings: a `<style>` with the `Orange.fr` webmail compatibility rule, a visible `<code>` element, and a hidden copy `<span>` (`display:none`). The hidden compatibility copy is excluded from plain-text conversion, so recipients receive the code once.
25
+
26
+ ```vue
27
+ <EText>
28
+ Run <ECodeInline class="inline-code">pnpm install</ECodeInline> to begin.
29
+ </EText>
30
+ ```
31
+
32
+ ## ECodeBlock
33
+
34
+ `ECodeBlock` is available only when `codeBlock` is configured on the Nuxt module. It uses the Shiki syntax highlighter and renders escaped tokens with inline theme colors. The language list is a closed build-time allowlist. Only configured languages and the grammars they import are bundled. A different runtime `language` value throws and lists the enabled languages.
35
+
36
+ | Prop | Type | Default | Required |
37
+ | --- | --- | --- | --- |
38
+ | `code` | `string` | — | Yes |
39
+ | `language` | Configured language union | — | Yes |
40
+ | `lineNumbers` | `boolean` | `false` | No |
41
+ | `fontFamily` | `string` | `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace` | No |
42
+
43
+ Code is escaped before rendering. The language is typed from the configured allowlist and checked again at runtime. Line numbers are decorative and excluded from plain text.
44
+
45
+ ```ts [nuxt.config.ts]
46
+ export default defineNuxtConfig({
47
+ modules: ['@lupinum/nuxt-email'],
48
+ nuxtEmail: {
49
+ codeBlock: {
50
+ languages: ['typescript', 'bash'],
51
+ theme: 'github-dark',
52
+ },
53
+ },
54
+ })
55
+ ```
56
+
57
+ ```vue
58
+ <ECodeBlock
59
+ :code="source"
60
+ language="typescript"
61
+ line-numbers
62
+ style="border-radius:8px;padding:16px"
63
+ />
64
+ ```
65
+
66
+ The configured theme produces concrete inline colors; there is no stylesheet or browser runtime. Author styles are also inline for email-client compatibility. Theme selection is build-time because email clients do not reliably support the CSS-variable approach used for responsive light/dark Shiki themes.
67
+
68
+ Without `codeBlock`, the component is unregistered and Shiki performs no module-setup or production-bundle work. Use the generated `#nuxt-email/testing` binding when testing templates that contain `ECodeBlock`.
@@ -0,0 +1,111 @@
1
+ ---
2
+ title: "Content"
3
+ description: "EText, EHeading, ELink, EImg, EHr, and EButton for visible email content."
4
+ url: "https://nuxt-email.lupinum.com/docs/components/content"
5
+ route: "/docs/components/content"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Content
13
+
14
+ > EText, EHeading, ELink, EImg, EHr, and EButton for visible email content.
15
+
16
+ These components carry the visible content of an email. Layout-sensitive components provide email-safe defaults. `ELink` is visually neutral.
17
+
18
+ ## EText
19
+
20
+ `EText` renders a `<p>` with `font-size: 14px`, `line-height: 24px`, and `16px` top and bottom margins. Author styles override these defaults.
21
+
22
+ | Prop | Type | Default | Required |
23
+ | --- | --- | --- | --- |
24
+ | — | No component-specific props; accepts [shared safe native attributes](/raw/docs/components.md#rules-every-component-shares). | — | — |
25
+
26
+ ```vue
27
+ <EText :style="{ color: '#334155' }">Your workspace is ready.</EText>
28
+ ```
29
+
30
+ ## EHeading
31
+
32
+ `EHeading` renders a heading element. An unsupported tag fails during rendering.
33
+
34
+ | Prop | Type | Default | Required |
35
+ | --- | --- | --- | --- |
36
+ | `as` | `'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6'` | `h1` | No |
37
+
38
+ It also accepts [shared safe native attributes](/raw/docs/components.md#rules-every-component-shares).
39
+
40
+ ```vue
41
+ <EHeading as="h2">Account ready</EHeading>
42
+ ```
43
+
44
+ ## ELink
45
+
46
+ `ELink` renders a visually neutral anchor. It adds no color or text decoration.
47
+
48
+ | Prop | Type | Default | Required |
49
+ | --- | --- | --- | --- |
50
+ | `href` | `string` | — | Yes |
51
+ | `target` | Safe native anchor target | `_blank` | No |
52
+
53
+ `href` must be non-empty. The value is escaped, but its URL scheme is not validated. Set visual styles explicitly and validate application-controlled URLs.
54
+
55
+ ```vue
56
+ <ELink href="https://example.com/account" style="color:#2563eb">Open account</ELink>
57
+ ```
58
+
59
+ ## EImg
60
+
61
+ `EImg` renders an image with block display and no border, outline, or text decoration.
62
+
63
+ | Prop | Type | Default | Required |
64
+ | --- | --- | --- | --- |
65
+ | `src` | `string` | — | Yes |
66
+ | `alt` | `string` | — | Yes |
67
+
68
+ `src` must be non-empty. Use `alt=""` for a decorative image. The URL is serialized but never fetched. Safe image attributes such as `width` and `height` are accepted.
69
+
70
+ ```vue
71
+ <EImg src="https://example.com/logo.png" alt="Example" width="120" height="32" />
72
+ ```
73
+
74
+ ## EHr
75
+
76
+ `EHr` renders a full-width horizontal rule with a transparent border and a `1px solid #eaeaea` top border. Author styles override individual defaults.
77
+
78
+ | Prop | Type | Default | Required |
79
+ | --- | --- | --- | --- |
80
+ | — | No component-specific props; accepts [shared safe native attributes](/raw/docs/components.md#rules-every-component-shares). | — | — |
81
+
82
+ ```vue
83
+ <EHr :style="{ borderTopColor: '#cbd5e1' }" />
84
+ ```
85
+
86
+ ## EButton
87
+
88
+ `EButton` renders an anchor with conditional Outlook spacer markup. The spacer keeps its padding usable in Outlook for Windows.
89
+
90
+ | Prop | Type | Default | Required |
91
+ | --- | --- | --- | --- |
92
+ | `href` | `string` | — | Yes |
93
+ | `target` | Safe native anchor target | `_blank` | No |
94
+ | `style` | Vue style value | Email-safe anchor defaults | No |
95
+
96
+ The default style uses `line-height: 100%`, no text decoration, inline-block display, and `max-width: 100%`. It also sets the Outlook-specific `mso-padding-alt` property to `0px`. Author styles override these values.
97
+
98
+ Padding accepts finite non-negative numbers or `px`, `em`, `rem`, and `%` values. Shorthand accepts one to four values. Invalid units, negative or non-finite values, and output large enough to inflate the message fail during rendering.
99
+
100
+ ```vue
101
+ <EButton
102
+ href="https://example.com/activate"
103
+ :style="{ backgroundColor: '#0f5132', color: '#fff', padding: '12px 20px' }"
104
+ >
105
+ Activate account
106
+ </EButton>
107
+ ```
108
+
109
+ <card title="Client notes" icon="lucide:mail">
110
+ Remote images are often blocked, so always provide useful alternative text. Outlook for Windows ignores CSS padding on inline anchors; `EButton` supplies conditional spacer markup for that client. Verify both behaviors during [manual QA](/raw/docs/guides/client-compatibility.md).
111
+ </card>
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: "Layout"
3
+ description: "EContainer, ESection, ERow, and EColumn — presentation-table primitives for stable email layouts."
4
+ url: "https://nuxt-email.lupinum.com/docs/components/layout"
5
+ route: "/docs/components/layout"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Layout
13
+
14
+ > EContainer, ESection, ERow, and EColumn — presentation-table primitives for stable email layouts.
15
+
16
+ These components use presentation tables for email-client compatibility.
17
+
18
+ ## EContainer
19
+
20
+ `EContainer` renders a centered presentation table for bounded content. It defaults to `align="center"`, `width="100%"`, and a maximum width of `37.5em`. Its fixed attributes are `border="0"`, `cellpadding="0"`, `cellspacing="0"`, and `role="presentation"`.
21
+
22
+ | Prop | Type | Default | Required |
23
+ | --- | --- | --- | --- |
24
+ | — | No component-specific props; accepts safe table attributes except the fixed attributes above. | — | — |
25
+
26
+ ```vue
27
+ <EContainer :style="{ maxWidth: '600px' }">Content</EContainer>
28
+ ```
29
+
30
+ ## ESection
31
+
32
+ `ESection` renders a full-width presentation table with one inner cell.
33
+
34
+ | Prop | Type | Default | Required |
35
+ | --- | --- | --- | --- |
36
+ | — | No component-specific props; accepts safe table attributes except `border`, `cellpadding`, `cellspacing`, and `role`. | — | — |
37
+
38
+ ```vue
39
+ <ESection :style="{ padding: '16px' }">Content</ESection>
40
+ ```
41
+
42
+ ## ERow
43
+
44
+ `ERow` renders a full-width presentation table whose row receives its default slot. Put `EColumn` components directly in the slot.
45
+
46
+ | Prop | Type | Default | Required |
47
+ | --- | --- | --- | --- |
48
+ | — | No component-specific props; accepts safe table attributes except `border`, `cellpadding`, `cellspacing`, and `role`. | — | — |
49
+
50
+ ```vue
51
+ <ERow><EColumn>Content</EColumn></ERow>
52
+ ```
53
+
54
+ ## EColumn
55
+
56
+ `EColumn` renders a `<td>` cell. It has no default width and adds no synthetic React marker.
57
+
58
+ | Prop | Type | Default | Required |
59
+ | --- | --- | --- | --- |
60
+ | — | No component-specific props; accepts safe cell attributes such as `width`, `colspan`, alignment, and `style`. | — | — |
61
+
62
+ ```vue
63
+ <EColumn width="50%">Left</EColumn>
64
+ ```
65
+
66
+ ## Padding placement
67
+
68
+ `EContainer`, `ESection`, and `ERow` move physical padding known at render time to a `<td>`. This includes author styles and non-variant Tailwind utilities. Responsive and pseudo-class utilities remain CSS rules because they have no single inline value. Put responsive padding on an inner `EColumn` and verify the target clients.
69
+
70
+ `ERow` adds its padding wrapper only when physical padding exists. Logical padding properties remain on the table.
71
+
72
+ ## Fixed presentation attributes
73
+
74
+ `EContainer`, `ESection`, and `ERow` reject overrides for `border`, `cellpadding`, `cellspacing`, and `role`, including alternate casing. Use `style` when you need a visual border. This deliberate [divergence](/raw/docs/reference/conformance.md) prevents silently broken layout semantics.
75
+
76
+ ```vue
77
+ <EContainer :style="{ maxWidth: '600px', padding: '24px' }">
78
+ <ESection :style="{ padding: '16px 20px' }">
79
+ <ERow>
80
+ <EColumn width="50%">Left</EColumn>
81
+ <EColumn width="50%">Right</EColumn>
82
+ </ERow>
83
+ </ESection>
84
+ </EContainer>
85
+ ```
86
+
87
+ <card title="Client note: Outlook columns" icon="lucide:columns-3">
88
+ Columns remain table cells in Outlook for Windows. Give them explicit widths and verify narrow layouts in your [client QA](/raw/docs/guides/client-compatibility.md).
89
+ </card>
@@ -0,0 +1,49 @@
1
+ ---
2
+ title: "Markdown"
3
+ description: "EMarkdown renders Markdown to email-safe HTML with inline styles and per-element overrides."
4
+ url: "https://nuxt-email.lupinum.com/docs/components/markdown"
5
+ route: "/docs/components/markdown"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Markdown
13
+
14
+ > EMarkdown renders Markdown to email-safe HTML with inline styles and per-element overrides.
15
+
16
+ ## EMarkdown
17
+
18
+ `EMarkdown` renders Markdown to email-safe HTML with inline styles. It is useful when content comes from a content management system (CMS) rather than Vue markup.
19
+
20
+ | Prop | Type | Default | Required |
21
+ | --- | --- | --- | --- |
22
+ | `source` | `string` | Text-only default slot | No |
23
+ | `markdownCustomStyles` | Per-element style map | Built-in email-safe styles | No |
24
+
25
+ When `source` is a string, it takes precedence over the default slot. Custom styles merge over the defaults.
26
+
27
+ Raw HTML is rejected, including HTML comments. Links and images accept relative URLs plus `http`, `https`, `mailto`, `tel`, and `cid`; other schemes are rejected after HTML-entity and control-character normalization. HTML-looking text inside code spans and fences is escaped. This keeps Markdown from becoming an implicit raw-HTML escape hatch.
28
+
29
+ The rendered container accepts Vue's normal `class`, `style`, and safe HTML attributes. Use ordinary string, object, or array `style` values; include CSS units explicitly for dimensional values such as padding.
30
+
31
+ The default slot must contain **text only**; an element child throws a `TypeError` rather than being silently stringified. Links render with `target="_blank"`, and tables render with `role="presentation"`.
32
+
33
+ ```vue
34
+ <EMarkdown source="# Title\n\nA paragraph with a [link](https://example.com)." />
35
+ ```
36
+
37
+ With overrides:
38
+
39
+ ```vue
40
+ <EMarkdown
41
+ :source="body"
42
+ :markdown-custom-styles="{ h1: { color: '#0f172a' }, a: { color: '#2563eb' } }"
43
+ :style="{ padding: '0 24px' }"
44
+ />
45
+ ```
46
+
47
+ <card title="Divergence: no data-id marker" icon="lucide:git-compare">
48
+ Unlike React Email, the container omits the `data-id="react-email-markdown"` marker (the same no-data-id divergence as `EColumn`). No Vue behavior consumes the marker. See the [conformance report](/raw/docs/reference/conformance.md).
49
+ </card>
@@ -0,0 +1,122 @@
1
+ ---
2
+ title: "Structure"
3
+ description: "EHtml, EHead, EBody, EPreview, and EFont — the document shell, head, body, inbox preview text, and font declarations."
4
+ url: "https://nuxt-email.lupinum.com/docs/components/structure"
5
+ route: "/docs/components/structure"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Structure
13
+
14
+ > EHtml, EHead, EBody, EPreview, and EFont — the document shell, head, body, inbox preview text, and font declarations.
15
+
16
+ These components form the email document shell. Every render must contain exactly one `<html>` root and one `<body>`.
17
+
18
+ ## EHtml
19
+
20
+ `EHtml` renders the required document root.
21
+
22
+ | Prop | Type | Default | Required |
23
+ | --- | --- | --- | --- |
24
+ | `lang` | `string` | — | Yes |
25
+ | `dir` | Safe native `dir` value | `ltr` | No |
26
+
27
+ `lang` must be a non-empty string. `EHtml` also accepts the [shared safe native attributes](/raw/docs/components.md#rules-every-component-shares).
28
+
29
+ ```vue
30
+ <EHtml lang="en"><EBody>Content</EBody></EHtml>
31
+ ```
32
+
33
+ ## EHead
34
+
35
+ `EHead` renders `<head>`. It inserts the UTF-8 content-type and Apple message-reformatting meta tags before its default slot. Put `<title>`, `EFont`, and other email head content in that slot.
36
+
37
+ | Prop | Type | Default | Required |
38
+ | --- | --- | --- | --- |
39
+ | — | No component-specific props; accepts [shared safe native attributes](/raw/docs/components.md#rules-every-component-shares). | — | — |
40
+
41
+ ```vue
42
+ <EHead><title>Order confirmed</title></EHead>
43
+ ```
44
+
45
+ ## EBody
46
+
47
+ `EBody` renders `<body>` and a full-width presentation table. Author styles apply to the inner cell. Background values mirror to `<body>`, where supplied margin and padding reset properties become zero.
48
+
49
+ | Prop | Type | Default | Required |
50
+ | --- | --- | --- | --- |
51
+ | `lang` | Safe native `lang` value | `en` | No |
52
+ | `dir` | Safe native `dir` value | `ltr` | No |
53
+
54
+ `EBody` accepts [shared safe native attributes](/raw/docs/components.md#rules-every-component-shares). Its language and direction do not inherit from `EHtml`; set both wrappers when the email uses another language or right-to-left text.
55
+
56
+ ```vue
57
+ <EBody :style="{ backgroundColor: '#f5f5f5' }">Content</EBody>
58
+ ```
59
+
60
+ ```vue
61
+ <EHtml lang="ar" dir="rtl">
62
+ <EHead>
63
+ <title>تم تأكيد الطلب</title>
64
+ </EHead>
65
+ <EBody lang="ar" dir="rtl">
66
+ <EText>تم تأكيد طلبك.</EText>
67
+ </EBody>
68
+ </EHtml>
69
+ ```
70
+
71
+ ### Localization and right-to-left text
72
+
73
+ Keep localization in application code. Either create one literal template per locale, such as `welcome/en` and `welcome/ar`, or pass already-resolved strings through typed props. Do not call application-context internationalization composables inside an email template. Email templates render in an isolated server application.
74
+
75
+ Set matching `lang` and `dir` values on both `EHtml` and `EBody`, as shown above. The wrappers do not inherit these values from one another.
76
+
77
+ ## EPreview
78
+
79
+ `EPreview` renders hidden inbox-preview text from a text-only default slot.
80
+
81
+ | Prop | Type | Default | Required |
82
+ | --- | --- | --- | --- |
83
+ | — | No component-specific props; accepts [shared safe native attributes](/raw/docs/components.md#rules-every-component-shares). | — | — |
84
+
85
+ Preview text is safely truncated at 200 UTF-16 code units. Compatibility filler is added when the text is shorter. Author styles cannot make the preview visible, and the preview is excluded from derived plain text.
86
+
87
+ ```vue
88
+ <EPreview>Your order is confirmed.</EPreview>
89
+ ```
90
+
91
+ Use an explicit `<title>` inside `EHead`. `EPreview` does not generate or move a title into the head. This is an [intentional divergence](/raw/docs/reference/conformance.md).
92
+
93
+ ## EFont
94
+
95
+ `EFont` declares an `@font-face` and a document-wide default font. Place it inside `EHead`.
96
+
97
+ | Prop | Type | Default | Required |
98
+ | --- | --- | --- | --- |
99
+ | `fontFamily` | `string` | — | Yes |
100
+ | `fallbackFontFamily` | `FontFallback \| readonly [FontFallback, ...FontFallback[]]` | — | Yes |
101
+ | `webFont` | `{ url: string, format: FontFormat }` | — | No |
102
+ | `fontStyle` | `'normal' \| 'italic' \| 'oblique'` | `normal` | No |
103
+ | `fontWeight` | `number \| 'normal' \| 'bold' \| 'bolder' \| 'lighter'` | `400` | No |
104
+
105
+ Supported fallbacks are `Arial`, `Helvetica`, `Verdana`, `Georgia`, `Times New Roman`, `serif`, `sans-serif`, `monospace`, `cursive`, and `fantasy`. Numeric weights must be from 1 through 1000. Web-font formats are `woff`, `woff2`, `truetype`, `opentype`, `embedded-opentype`, and `svg`.
106
+
107
+ `EFont` emits one `<style>` with the font-face rule and a global font stack. Outlook uses the first fallback through its specific `mso-font-alt` property. Invalid or empty values fail during rendering.
108
+
109
+ ```vue
110
+ <EHead>
111
+ <EFont
112
+ font-family="Roboto"
113
+ :fallback-font-family="['Verdana', 'sans-serif']"
114
+ :web-font="{ url: 'https://fonts.example.com/roboto.woff2', format: 'woff2' }"
115
+ :font-weight="700"
116
+ />
117
+ </EHead>
118
+ ```
119
+
120
+ <card title="Client note" icon="lucide:mail">
121
+ Only some clients honor web fonts. Choose a fallback stack that resembles the primary font and verify it in the clients you support.
122
+ </card>