better-svelte-email 0.0.1
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 +422 -0
- package/dist/components/Body.svelte +9 -0
- package/dist/components/Body.svelte.d.ts +13 -0
- package/dist/components/Button.svelte +54 -0
- package/dist/components/Button.svelte.d.ts +21 -0
- package/dist/components/Container.svelte +28 -0
- package/dist/components/Container.svelte.d.ts +13 -0
- package/dist/components/Head.svelte +13 -0
- package/dist/components/Head.svelte.d.ts +6 -0
- package/dist/components/Html.svelte +19 -0
- package/dist/components/Html.svelte.d.ts +10 -0
- package/dist/components/Section.svelte +21 -0
- package/dist/components/Section.svelte.d.ts +13 -0
- package/dist/components/Text.svelte +17 -0
- package/dist/components/Text.svelte.d.ts +15 -0
- package/dist/components/__tests__/test-email.svelte +13 -0
- package/dist/components/__tests__/test-email.svelte.d.ts +26 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.js +9 -0
- package/dist/emails/demo-email.svelte +108 -0
- package/dist/emails/demo-email.svelte.d.ts +13 -0
- package/dist/emails/test-email.svelte +15 -0
- package/dist/emails/test-email.svelte.d.ts +26 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +10 -0
- package/dist/preprocessor/head-injector.d.ts +9 -0
- package/dist/preprocessor/head-injector.js +57 -0
- package/dist/preprocessor/index.d.ts +25 -0
- package/dist/preprocessor/index.js +196 -0
- package/dist/preprocessor/parser.d.ts +14 -0
- package/dist/preprocessor/parser.js +249 -0
- package/dist/preprocessor/transformer.d.ts +18 -0
- package/dist/preprocessor/transformer.js +158 -0
- package/dist/preprocessor/types.d.ts +104 -0
- package/dist/preprocessor/types.js +1 -0
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.js +24 -0
- package/package.json +97 -0
package/README.md
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
# better-svelte-email
|
|
2
|
+
|
|
3
|
+
[](https://github.com/YOUR_USERNAME/better-svelte-email/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/YOUR_USERNAME/better-svelte-email/actions/workflows/release.yml)
|
|
5
|
+
|
|
6
|
+
A Svelte 5 preprocessor that transforms Tailwind CSS classes in email components to inline styles with responsive media query support.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
✨ **Stable & Future-Proof** - Uses Svelte 5's public preprocessor API
|
|
11
|
+
🎨 **Tailwind CSS Support** - Transforms Tailwind classes to inline styles for email clients
|
|
12
|
+
📱 **Responsive Emails** - Preserves responsive classes (`sm:`, `md:`, `lg:`) as media queries
|
|
13
|
+
⚡ **Build-Time Transformation** - Zero runtime overhead
|
|
14
|
+
🔍 **TypeScript First** - Fully typed with comprehensive type definitions
|
|
15
|
+
✅ **Well Tested** - Extensive test coverage with unit and integration tests
|
|
16
|
+
|
|
17
|
+
## Why?
|
|
18
|
+
|
|
19
|
+
Email clients don't support modern CSS in `<style>` tags, requiring inline styles. But writing inline styles is tedious and hard to maintain. This preprocessor lets you write Tailwind CSS classes and automatically transforms them to inline styles at build time.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install better-svelte-email
|
|
25
|
+
# or
|
|
26
|
+
bun add better-svelte-email
|
|
27
|
+
# or
|
|
28
|
+
pnpm add better-svelte-email
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
### 1. Configure the Preprocessor
|
|
34
|
+
|
|
35
|
+
Add the preprocessor to your `svelte.config.js`:
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
39
|
+
import { betterSvelteEmailPreprocessor } from 'better-svelte-email';
|
|
40
|
+
|
|
41
|
+
/** @type {import('@sveltejs/kit').Config} */
|
|
42
|
+
const config = {
|
|
43
|
+
preprocess: [
|
|
44
|
+
vitePreprocess(),
|
|
45
|
+
betterSvelteEmailPreprocessor({
|
|
46
|
+
pathToEmailFolder: '/src/lib/emails',
|
|
47
|
+
debug: false
|
|
48
|
+
})
|
|
49
|
+
],
|
|
50
|
+
kit: {
|
|
51
|
+
adapter: adapter()
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default config;
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. Create Email Components
|
|
59
|
+
|
|
60
|
+
Create your email templates in `src/lib/emails/`:
|
|
61
|
+
|
|
62
|
+
```svelte
|
|
63
|
+
<!-- src/lib/emails/welcome.svelte -->
|
|
64
|
+
<script>
|
|
65
|
+
let { name = 'User' } = $props();
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<Html>
|
|
69
|
+
<Head />
|
|
70
|
+
<Body class="bg-gray-100">
|
|
71
|
+
<Container class="mx-auto p-8">
|
|
72
|
+
<Text class="mb-4 text-2xl font-bold">
|
|
73
|
+
Welcome, {name}!
|
|
74
|
+
</Text>
|
|
75
|
+
|
|
76
|
+
<Button
|
|
77
|
+
href="https://example.com"
|
|
78
|
+
class="rounded bg-blue-600 px-6 py-3 text-white sm:bg-green-600"
|
|
79
|
+
>
|
|
80
|
+
Get Started
|
|
81
|
+
</Button>
|
|
82
|
+
</Container>
|
|
83
|
+
</Body>
|
|
84
|
+
</Html>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3. Render and Send
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// src/routes/api/send-email/+server.ts
|
|
91
|
+
import { render } from 'svelte/server';
|
|
92
|
+
import WelcomeEmail from '$lib/emails/welcome.svelte';
|
|
93
|
+
|
|
94
|
+
export async function POST({ request }) {
|
|
95
|
+
const { name, email } = await request.json();
|
|
96
|
+
|
|
97
|
+
// Render email (preprocessor already ran at build time!)
|
|
98
|
+
// In Svelte 5, pass props directly to render
|
|
99
|
+
const result = render(WelcomeEmail, { props: { name } });
|
|
100
|
+
|
|
101
|
+
// Send email using your preferred service (Resend, SendGrid, etc.)
|
|
102
|
+
// await resend.emails.send({
|
|
103
|
+
// from: 'noreply@example.com',
|
|
104
|
+
// to: email,
|
|
105
|
+
// subject: 'Welcome!',
|
|
106
|
+
// html: result.body
|
|
107
|
+
// });
|
|
108
|
+
|
|
109
|
+
return new Response('Sent');
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## How It Works
|
|
114
|
+
|
|
115
|
+
The preprocessor transforms your Tailwind classes in three steps:
|
|
116
|
+
|
|
117
|
+
### 1. Non-Responsive Classes → Inline Styles
|
|
118
|
+
|
|
119
|
+
```svelte
|
|
120
|
+
<!-- Input -->
|
|
121
|
+
<Button class="bg-blue-500 p-4 text-white">Click</Button>
|
|
122
|
+
|
|
123
|
+
<!-- Output -->
|
|
124
|
+
<Button
|
|
125
|
+
styleString="background-color: rgb(59, 130, 246); color: rgb(255, 255, 255); padding: 16px;"
|
|
126
|
+
>
|
|
127
|
+
Click
|
|
128
|
+
</Button>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 2. Responsive Classes → Media Queries
|
|
132
|
+
|
|
133
|
+
```svelte
|
|
134
|
+
<!-- Input -->
|
|
135
|
+
<Button class="bg-blue-500 sm:bg-red-500">Click</Button>
|
|
136
|
+
|
|
137
|
+
<!-- Output -->
|
|
138
|
+
<Button class="sm_bg-red-500" styleString="background-color: rgb(59, 130, 246);">Click</Button>
|
|
139
|
+
|
|
140
|
+
<!-- Injected into <Head> -->
|
|
141
|
+
<style>
|
|
142
|
+
@media (max-width: 475px) {
|
|
143
|
+
.sm_bg-red-500 {
|
|
144
|
+
background-color: rgb(239, 68, 68) !important;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
</style>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 3. Mixed Classes → Both
|
|
151
|
+
|
|
152
|
+
```svelte
|
|
153
|
+
<!-- Input -->
|
|
154
|
+
<Button class="rounded bg-blue-500 p-4 sm:bg-red-500 md:p-6">Click</Button>
|
|
155
|
+
|
|
156
|
+
<!-- Output -->
|
|
157
|
+
<Button
|
|
158
|
+
class="sm_bg-red-500 md_p-6"
|
|
159
|
+
styleString="border-radius: 4px; background-color: rgb(59, 130, 246); padding: 16px;"
|
|
160
|
+
>
|
|
161
|
+
Click
|
|
162
|
+
</Button>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Configuration
|
|
166
|
+
|
|
167
|
+
### Options
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
interface PreprocessorOptions {
|
|
171
|
+
/**
|
|
172
|
+
* Path to folder containing email components
|
|
173
|
+
* @default '/src/lib/emails'
|
|
174
|
+
*/
|
|
175
|
+
pathToEmailFolder?: string;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Custom Tailwind configuration
|
|
179
|
+
* @default undefined
|
|
180
|
+
*/
|
|
181
|
+
tailwindConfig?: TailwindConfig;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Enable debug logging
|
|
185
|
+
* @default false
|
|
186
|
+
*/
|
|
187
|
+
debug?: boolean;
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Custom Tailwind Config
|
|
192
|
+
|
|
193
|
+
```javascript
|
|
194
|
+
betterSvelteEmailPreprocessor({
|
|
195
|
+
pathToEmailFolder: '/src/lib/emails',
|
|
196
|
+
tailwindConfig: {
|
|
197
|
+
theme: {
|
|
198
|
+
extend: {
|
|
199
|
+
colors: {
|
|
200
|
+
brand: '#FF3E00'
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Supported Features
|
|
209
|
+
|
|
210
|
+
### ✅ Supported
|
|
211
|
+
|
|
212
|
+
- ✅ Static Tailwind classes
|
|
213
|
+
- ✅ All standard Tailwind utilities (colors, spacing, typography, etc.)
|
|
214
|
+
- ✅ Responsive breakpoints (`sm:`, `md:`, `lg:`, `xl:`, `2xl:`)
|
|
215
|
+
- ✅ HTML elements and Svelte components
|
|
216
|
+
- ✅ Nested components
|
|
217
|
+
- ✅ Conditional blocks (`{#if}`)
|
|
218
|
+
- ✅ Each blocks (`{#each}`)
|
|
219
|
+
- ✅ Custom Tailwind configurations
|
|
220
|
+
|
|
221
|
+
### ❌ Not Supported (Yet)
|
|
222
|
+
|
|
223
|
+
- ❌ Dynamic class expressions (`class={someVar}`)
|
|
224
|
+
- ❌ Arbitrary values in responsive classes (`sm:[color:red]`)
|
|
225
|
+
- ❌ Container queries
|
|
226
|
+
|
|
227
|
+
## API Reference
|
|
228
|
+
|
|
229
|
+
### Main Export
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { betterSvelteEmailPreprocessor } from 'better-svelte-email';
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Advanced Exports
|
|
236
|
+
|
|
237
|
+
For advanced use cases, you can use individual functions:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import {
|
|
241
|
+
parseClassAttributes,
|
|
242
|
+
transformTailwindClasses,
|
|
243
|
+
generateMediaQueries,
|
|
244
|
+
injectMediaQueries
|
|
245
|
+
} from 'better-svelte-email';
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Testing
|
|
249
|
+
|
|
250
|
+
The library includes comprehensive tests:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# Run all tests
|
|
254
|
+
npm test
|
|
255
|
+
|
|
256
|
+
# Run tests in watch mode
|
|
257
|
+
npm run test:watch
|
|
258
|
+
|
|
259
|
+
# Run tests with UI
|
|
260
|
+
npm run test:ui
|
|
261
|
+
|
|
262
|
+
# Run tests with coverage
|
|
263
|
+
npm run test:coverage
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Examples
|
|
267
|
+
|
|
268
|
+
### Simple Button
|
|
269
|
+
|
|
270
|
+
```svelte
|
|
271
|
+
<Button class="rounded bg-blue-500 px-4 py-2 text-white">Click Me</Button>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Responsive Layout
|
|
275
|
+
|
|
276
|
+
```svelte
|
|
277
|
+
<Container class="mx-auto w-full max-w-2xl p-4 sm:p-6 md:p-8">
|
|
278
|
+
<Text class="text-lg sm:text-xl md:text-2xl">Responsive Text</Text>
|
|
279
|
+
</Container>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Complex Email
|
|
283
|
+
|
|
284
|
+
```svelte
|
|
285
|
+
<Html>
|
|
286
|
+
<Head />
|
|
287
|
+
<Body class="bg-gray-100 font-sans">
|
|
288
|
+
<Container class="mx-auto my-8 max-w-2xl rounded-lg bg-white shadow-lg">
|
|
289
|
+
<Section class="p-8">
|
|
290
|
+
<Text class="mb-4 text-3xl font-bold text-gray-900">Welcome to Our Service</Text>
|
|
291
|
+
|
|
292
|
+
<Text class="mb-6 text-gray-600">
|
|
293
|
+
Thank you for signing up. We're excited to have you on board!
|
|
294
|
+
</Text>
|
|
295
|
+
|
|
296
|
+
<Button
|
|
297
|
+
href="https://example.com/verify"
|
|
298
|
+
class="rounded-lg bg-blue-600 px-8 py-4 font-semibold text-white sm:bg-green-600"
|
|
299
|
+
>
|
|
300
|
+
Verify Your Email
|
|
301
|
+
</Button>
|
|
302
|
+
</Section>
|
|
303
|
+
</Container>
|
|
304
|
+
</Body>
|
|
305
|
+
</Html>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Troubleshooting
|
|
309
|
+
|
|
310
|
+
### Classes not being transformed
|
|
311
|
+
|
|
312
|
+
1. Make sure your file is in the configured `pathToEmailFolder`
|
|
313
|
+
2. Check that your classes are static strings, not dynamic expressions
|
|
314
|
+
3. Enable debug mode to see warnings: `{ debug: true }`
|
|
315
|
+
|
|
316
|
+
### Media queries not working
|
|
317
|
+
|
|
318
|
+
1. Ensure you have a `<Head />` component in your email
|
|
319
|
+
2. Check that you're using standard breakpoints (`sm:`, `md:`, etc.)
|
|
320
|
+
3. Verify the media queries are being injected (view the rendered HTML)
|
|
321
|
+
|
|
322
|
+
### Type errors
|
|
323
|
+
|
|
324
|
+
Make sure you have the latest version of Svelte 5:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
npm install svelte@^5.0.0
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Contributing
|
|
331
|
+
|
|
332
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
333
|
+
|
|
334
|
+
## License
|
|
335
|
+
|
|
336
|
+
MIT
|
|
337
|
+
|
|
338
|
+
## Author
|
|
339
|
+
|
|
340
|
+
Anatole
|
|
341
|
+
|
|
342
|
+
## Svelte 5 Syntax
|
|
343
|
+
|
|
344
|
+
This library requires **Svelte 5** and uses modern Svelte 5 syntax. Key differences from Svelte 4:
|
|
345
|
+
|
|
346
|
+
### Props Declaration
|
|
347
|
+
|
|
348
|
+
```svelte
|
|
349
|
+
<!-- Svelte 5 (Correct) -->
|
|
350
|
+
<script>
|
|
351
|
+
let { name = 'User', email } = $props();
|
|
352
|
+
</script>
|
|
353
|
+
|
|
354
|
+
<!-- Svelte 4 (Old - Don't use) -->
|
|
355
|
+
<script>
|
|
356
|
+
export let name = 'User';
|
|
357
|
+
export let email;
|
|
358
|
+
</script>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Server Rendering
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
// Svelte 5
|
|
365
|
+
import { render } from 'svelte/server';
|
|
366
|
+
const result = render(Component, { props: { name } });
|
|
367
|
+
const html = result.body;
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
For a complete guide on Svelte 5 syntax, see [SVELTE5_MIGRATION.md](./SVELTE5_MIGRATION.md).
|
|
371
|
+
|
|
372
|
+
## Development
|
|
373
|
+
|
|
374
|
+
### Running Tests
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
bun test
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
All tests must pass before pushing to main. The CI/CD pipeline will automatically run tests on every push and pull request.
|
|
381
|
+
|
|
382
|
+
### Building
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
bun run build
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Contributing
|
|
389
|
+
|
|
390
|
+
1. Fork the repository
|
|
391
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
392
|
+
3. Make your changes
|
|
393
|
+
4. Run tests (`bun test`)
|
|
394
|
+
5. Commit your changes using [conventional commits](https://www.conventionalcommits.org/):
|
|
395
|
+
- `feat:` - New features
|
|
396
|
+
- `fix:` - Bug fixes
|
|
397
|
+
- `docs:` - Documentation changes
|
|
398
|
+
- `test:` - Test additions/changes
|
|
399
|
+
- `chore:` - Maintenance tasks
|
|
400
|
+
6. Push to your branch (`git push origin feature/amazing-feature`)
|
|
401
|
+
7. Open a Pull Request
|
|
402
|
+
|
|
403
|
+
### Releases
|
|
404
|
+
|
|
405
|
+
Releases are automated via GitHub Actions. When you bump the version in `package.json` and push to `main`, a new release will be automatically created with a generated changelog.
|
|
406
|
+
|
|
407
|
+
See [RELEASE.md](./RELEASE.md) for detailed release process documentation.
|
|
408
|
+
|
|
409
|
+
## Acknowledgments
|
|
410
|
+
|
|
411
|
+
- Built on top of [Svelte 5](https://svelte.dev/)
|
|
412
|
+
- Uses [tw-to-css](https://github.com/dvkndn/tw-to-css) for Tailwind to CSS conversion
|
|
413
|
+
- Uses [magic-string](https://github.com/rich-harris/magic-string) for efficient source transformations
|
|
414
|
+
|
|
415
|
+
## Related Projects
|
|
416
|
+
|
|
417
|
+
- [react-email](https://react.email/) - React version for email templates
|
|
418
|
+
- [svelte-email](https://github.com/carstenlebek/svelte-email) - Original Svelte email library
|
|
419
|
+
|
|
420
|
+
## Support
|
|
421
|
+
|
|
422
|
+
If you find this project useful, please consider giving it a ⭐️ on GitHub!
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default Body;
|
|
2
|
+
type Body = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Body: import("svelte").Component<{
|
|
7
|
+
styleString?: string;
|
|
8
|
+
children: any;
|
|
9
|
+
} & Record<string, any>, {}, "">;
|
|
10
|
+
type $$ComponentProps = {
|
|
11
|
+
styleString?: string;
|
|
12
|
+
children: any;
|
|
13
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { styleToString, pxToPt } from '../utils/index.js';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
href = '#',
|
|
6
|
+
target = '_blank',
|
|
7
|
+
styleString = '',
|
|
8
|
+
pX = 0,
|
|
9
|
+
pY = 0,
|
|
10
|
+
children,
|
|
11
|
+
...restProps
|
|
12
|
+
} = $props();
|
|
13
|
+
|
|
14
|
+
const y = pY * 2;
|
|
15
|
+
const textRaise = pxToPt(y.toString());
|
|
16
|
+
|
|
17
|
+
// Button link styles
|
|
18
|
+
const buttonStyle = styleToString({
|
|
19
|
+
lineHeight: '100%',
|
|
20
|
+
textDecoration: 'none',
|
|
21
|
+
display: 'inline-block',
|
|
22
|
+
maxWidth: '100%',
|
|
23
|
+
padding: pY || pX ? `${pY}px ${pX}px` : undefined
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Button text styles with MSO support
|
|
27
|
+
const buttonTextStyle = styleToString({
|
|
28
|
+
maxWidth: '100%',
|
|
29
|
+
display: 'inline-block',
|
|
30
|
+
lineHeight: '120%',
|
|
31
|
+
textDecoration: 'none',
|
|
32
|
+
textTransform: 'none',
|
|
33
|
+
msoPaddingAlt: '0px',
|
|
34
|
+
msoTextRaise: pY ? pxToPt(pY.toString()) : undefined
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const finalStyle = buttonStyle + (styleString ? ';' + styleString : '');
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<a {...restProps} {href} {target} style={finalStyle}>
|
|
41
|
+
{#if pX}
|
|
42
|
+
<span>
|
|
43
|
+
{@html `<!--[if mso]><i style="letter-spacing: ${pX}px;mso-font-width:-100%;mso-text-raise:${textRaise}" hidden> </i><![endif]-->`}
|
|
44
|
+
</span>
|
|
45
|
+
{/if}
|
|
46
|
+
<span style={buttonTextStyle}>
|
|
47
|
+
{@render children?.()}
|
|
48
|
+
</span>
|
|
49
|
+
{#if pX}
|
|
50
|
+
<span>
|
|
51
|
+
{@html `<!--[if mso]><i style="letter-spacing: ${pX}px;mso-font-width:-100%" hidden> </i><![endif]-->`}
|
|
52
|
+
</span>
|
|
53
|
+
{/if}
|
|
54
|
+
</a>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export default Button;
|
|
2
|
+
type Button = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Button: import("svelte").Component<{
|
|
7
|
+
href?: string;
|
|
8
|
+
target?: string;
|
|
9
|
+
styleString?: string;
|
|
10
|
+
pX?: number;
|
|
11
|
+
pY?: number;
|
|
12
|
+
children: any;
|
|
13
|
+
} & Record<string, any>, {}, "">;
|
|
14
|
+
type $$ComponentProps = {
|
|
15
|
+
href?: string;
|
|
16
|
+
target?: string;
|
|
17
|
+
styleString?: string;
|
|
18
|
+
pX?: number;
|
|
19
|
+
pY?: number;
|
|
20
|
+
children: any;
|
|
21
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { styleToString } from '../utils/index.js';
|
|
3
|
+
|
|
4
|
+
let { styleString = '', children, ...restProps } = $props();
|
|
5
|
+
|
|
6
|
+
// Default max-width for email containers (600px = 37.5em)
|
|
7
|
+
const baseStyle = styleToString({ maxWidth: '37.5em' });
|
|
8
|
+
const finalStyle = baseStyle + (styleString ? ';' + styleString : '');
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<table
|
|
12
|
+
align="center"
|
|
13
|
+
width="100%"
|
|
14
|
+
role="presentation"
|
|
15
|
+
cellspacing="0"
|
|
16
|
+
cellpadding="0"
|
|
17
|
+
border="0"
|
|
18
|
+
{...restProps}
|
|
19
|
+
style={finalStyle}
|
|
20
|
+
>
|
|
21
|
+
<tbody>
|
|
22
|
+
<tr style="width: 100%;">
|
|
23
|
+
<td>
|
|
24
|
+
{@render children?.()}
|
|
25
|
+
</td>
|
|
26
|
+
</tr>
|
|
27
|
+
</tbody>
|
|
28
|
+
</table>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default Container;
|
|
2
|
+
type Container = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Container: import("svelte").Component<{
|
|
7
|
+
styleString?: string;
|
|
8
|
+
children: any;
|
|
9
|
+
} & Record<string, any>, {}, "">;
|
|
10
|
+
type $$ComponentProps = {
|
|
11
|
+
styleString?: string;
|
|
12
|
+
children: any;
|
|
13
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let { children }: { children?: any } = $props();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<head>
|
|
6
|
+
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
|
+
<meta name="x-apple-disable-message-reformatting" />
|
|
9
|
+
<!--[if !mso]><!-->
|
|
10
|
+
<meta http-equiv="x-ua-compatible" content="IE=edge" />
|
|
11
|
+
<!--<![endif]-->
|
|
12
|
+
{@render children?.()}
|
|
13
|
+
</head>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
lang?: string;
|
|
4
|
+
dir?: 'ltr' | 'rtl' | 'auto' | null | undefined;
|
|
5
|
+
styleString?: string;
|
|
6
|
+
children?: any;
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let { lang = 'en', dir = 'ltr', styleString = '', children, ...restProps }: Props = $props();
|
|
11
|
+
|
|
12
|
+
const doctype =
|
|
13
|
+
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
{@html doctype}
|
|
17
|
+
<html {...restProps} id="__svelte-email" {lang} {dir} style={styleString}>
|
|
18
|
+
{@render children?.()}
|
|
19
|
+
</html>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
lang?: string;
|
|
3
|
+
dir?: 'ltr' | 'rtl' | 'auto' | null | undefined;
|
|
4
|
+
styleString?: string;
|
|
5
|
+
children?: any;
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
}
|
|
8
|
+
declare const Html: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type Html = ReturnType<typeof Html>;
|
|
10
|
+
export default Html;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
let { styleString = '', children, ...restProps } = $props();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<table
|
|
6
|
+
width="100%"
|
|
7
|
+
role="presentation"
|
|
8
|
+
cellspacing="0"
|
|
9
|
+
cellpadding="0"
|
|
10
|
+
border="0"
|
|
11
|
+
{...restProps}
|
|
12
|
+
style={styleString}
|
|
13
|
+
>
|
|
14
|
+
<tbody>
|
|
15
|
+
<tr>
|
|
16
|
+
<td>
|
|
17
|
+
{@render children?.()}
|
|
18
|
+
</td>
|
|
19
|
+
</tr>
|
|
20
|
+
</tbody>
|
|
21
|
+
</table>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default Section;
|
|
2
|
+
type Section = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Section: import("svelte").Component<{
|
|
7
|
+
styleString?: string;
|
|
8
|
+
children: any;
|
|
9
|
+
} & Record<string, any>, {}, "">;
|
|
10
|
+
type $$ComponentProps = {
|
|
11
|
+
styleString?: string;
|
|
12
|
+
children: any;
|
|
13
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { styleToString } from '../utils/index.js';
|
|
3
|
+
|
|
4
|
+
let { as = 'p', styleString = '', children, ...restProps } = $props();
|
|
5
|
+
|
|
6
|
+
// Default email-safe text styles
|
|
7
|
+
const baseStyle = styleToString({
|
|
8
|
+
fontSize: '14px',
|
|
9
|
+
lineHeight: '24px',
|
|
10
|
+
margin: '16px 0'
|
|
11
|
+
});
|
|
12
|
+
const finalStyle = baseStyle + (styleString ? ';' + styleString : '');
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<svelte:element this={as} {...restProps} style={finalStyle}>
|
|
16
|
+
{@render children?.()}
|
|
17
|
+
</svelte:element>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default Text;
|
|
2
|
+
type Text = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Text: import("svelte").Component<{
|
|
7
|
+
as?: string;
|
|
8
|
+
styleString?: string;
|
|
9
|
+
children: any;
|
|
10
|
+
} & Record<string, any>, {}, "">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
as?: string;
|
|
13
|
+
styleString?: string;
|
|
14
|
+
children: any;
|
|
15
|
+
} & Record<string, any>;
|