Skip to content

Commit

Permalink
feat(jsx-email): import css in templates
Browse files Browse the repository at this point in the history
  • Loading branch information
shellscape committed Nov 21, 2024
1 parent d731f47 commit 685ff98
Show file tree
Hide file tree
Showing 15 changed files with 226 additions and 16 deletions.
1 change: 1 addition & 0 deletions apps/web/.vitepress/sidebar.mts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const sidebar = [
items: [
{ text: 'Introduction', link: '/docs/introduction' },
{ text: 'Quick Start', link: '/docs/quick-start' },
{ text: 'Recipes', link: '/docs/recipes' },
{ text: 'Email Providers', link: '/docs/email-providers' },
{ text: 'Email Samples', link: 'https://samples.jsx.email' },
{ text: 'FAQ', link: '/docs/faq' },
Expand Down
10 changes: 10 additions & 0 deletions apps/web/.vitepress/theme/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,13 @@ img.clients {
height: auto;
}
}

table.recipes {
border-collapse: collapse;
}

table.recipes td {
border: 0 !important;
padding: 0 10px 0 0 !important;
white-space: nowrap;
}
21 changes: 21 additions & 0 deletions docs/recipes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: 'Recipes'
description: 'Recipes for jsx-email'
---

## 🧁 jsx-email Recipes

Recipes are actual, working examples of jsx-email features and techniques that can be copied and pasted. They help users to understand how a thing works outside of the documentation.

Available recipes:

<table border="0" cellspacing="0" cellpadding="0" class="recipes">
<tr>
<td>🧁</td>
<td>Importing CSS</td>
<td width="100%"><a href="https://github.com/shellscape/jsx-email/tree/main/recipes/import-css">/recipes/import-css</a></td>
</tr>
</table>

<br/><br/><br/><br/><br/><br/><br/>
Don't see a recipe that you'd like to? [Open an Issue!](https://github.com/shellscape/jsx-email/issues/new?assignees=&labels=&projects=&template=DOCS.md)
16 changes: 14 additions & 2 deletions packages/jsx-email/src/cli/commands/build.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { existsSync } from 'node:fs';
import { mkdir, realpath, writeFile } from 'node:fs/promises';
import { mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
import os from 'node:os';
import { dirname, basename, extname, join, resolve, win32, posix } from 'path';
import { pathToFileURL } from 'url';
Expand All @@ -17,9 +17,9 @@ import { parse as assert, boolean, object, optional, string } from 'valibot';

import { log } from '../../log.js';
import { formatBytes, gmailByteLimit, originalCwd } from '../helpers.mjs';
import { loadConfig } from '../../config.js';

import type { CommandFn, TemplateFn } from './types.mjs';
import { loadConfig } from '../../config.js';

const BuildCommandOptionsStruct = object({
exclude: optional(string()),
Expand Down Expand Up @@ -143,6 +143,17 @@ export const build = async (options: BuildOptions): Promise<BuildResult> => {
};
};

const cssPlugin: esbuild.Plugin = {
name: 'jsx-email/css-plugin',
setup(builder) {
builder.onLoad({ filter: /\.css$/ }, async (args) => {
const buffer = await readFile(args.path);
const css = await esbuild.transform(buffer, { loader: 'css', minify: false });
return { contents: css.code, loader: 'text' };
});
}
};

const compile = async (options: CompileOptions) => {
const config = await loadConfig();

Expand All @@ -159,6 +170,7 @@ const compile = async (options: CompileOptions) => {
metafile: true,
outdir: outDir,
platform: 'node',
plugins: [cssPlugin],
write: true,
...config.esbuild
});
Expand Down
13 changes: 0 additions & 13 deletions packages/jsx-email/src/declarations.d.ts

This file was deleted.

11 changes: 11 additions & 0 deletions packages/jsx-email/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,14 @@ export interface RenderOptions {
export interface ProcessOptions extends Required<Omit<RenderOptions, 'plainText'>> {
html: string;
}

declare global {
namespace globalThis {
// eslint-disable-next-line vars-on-top, no-var
var isJsxEmailPreview: boolean;
}

interface ImportMeta {
isJsxEmailPreview: boolean;
}
}
2 changes: 1 addition & 1 deletion packages/jsx-email/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"extends": "../../shared/tsconfig.base.json",
"include": ["src"]
"include": ["src/**/*.ts", "src/**/*.d.ts"]
}
1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ packages:
- 'apps/*'
- 'packages/*'
- 'test/*'
- '!recipes/*'
12 changes: 12 additions & 0 deletions recipes/import-css/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

node_modules

# env
.env

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
15 changes: 15 additions & 0 deletions recipes/import-css/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 🧁 Importing CSS

If you're using a design system based on Styled Components or similar, and aren't using Tailwind, you may have the need to export that design system as CSS for use in jsx-email` templates. Or, you may be raw-dogging CSS straight up. For either scenario, importing CSS directly may be useful when crafting your email templates. jsx-email ships with the ability to import CSS as a string for use in your templates.

This recipe contains code to demonstrate how.

## Run the Recipe

To run this recipe, please open the terminal or console of your choice, and navigate to the directory this file resides in. Then, run:

```shell
$ npm i && npm run dev
```

Once the preview app opens, select the `Import CSS` template and examine the HTML and JSX code tabs to view the source and result of a CSS import.
19 changes: 19 additions & 0 deletions recipes/import-css/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "email-project",
"version": "0.0.0",
"private": true,
"description": "A simple starter for jsx-email",
"scripts": {
"build": "email build ./templates",
"create": "email create",
"dev": "email preview ./templates"
},
"dependencies": {
"jsx-email": "^2.0.0"
},
"devDependencies": {
"@types/react": "^18.2.0",
"react": "^18.2.0",
"typescript": "^5.2.2"
}
}
3 changes: 3 additions & 0 deletions recipes/import-css/templates/email.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
background: #eee !important;
}
93 changes: 93 additions & 0 deletions recipes/import-css/templates/email.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Body, Button, Container, Head, Hr, Html, Link, Preview, Section, Text } from 'jsx-email';

import css from './email.css';

interface TemplateProps {
email: string;
name: string;
}

const main = {
backgroundColor: '#f6f9fc',
fontFamily:
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif'
};

const container = {
backgroundColor: '#ffffff',
margin: '0 auto',
marginBottom: '64px',
padding: '20px 0 48px'
};

const box = {
padding: '0 48px'
};

const hr = {
borderColor: '#e6ebf1',
margin: '20px 0'
};

const paragraph = {
color: '#777',
fontSize: '16px',
lineHeight: '24px',
textAlign: 'left' as const
};

const anchor = {
color: '#777'
};

const button = {
fontWeight: 'bold',
padding: '10px',
textDecoration: 'none'
};

export const previewProps: TemplateProps = {
email: '[email protected]',
name: 'Bruce Wayne'
};

export const templateName = 'Import CSS';

export const Template = ({ email, name }: TemplateProps) => (
<Html>
<Head>
<style>{css as string}</style>
</Head>
<Preview>
This is our email preview text for {name} &lt;{email}&gt;
</Preview>
<Body style={main}>
<Container style={container}>
<Section style={box}>
<Text style={paragraph}>This is our email body text</Text>
<Button
align={'center'}
backgroundColor={'#777'}
borderRadius={5}
fontSize={16}
height={60}
href="https://example.com"
style={button}
textColor={'#fff'}
width={160}
>
Action Button
</Button>
<Hr style={hr} />
<Text style={paragraph}>
This is text content with a{' '}
<Link style={anchor} href="mailto:{email}">
link
</Link>
.
</Text>
</Section>
</Container>
</Body>
</Html>
);
24 changes: 24 additions & 0 deletions recipes/import-css/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"lib": ["ES2023"],
"module": "ESNext",
"moduleResolution": "node",
"noEmitOnError": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"preserveSymlinks": true,
"preserveWatchOutput": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"strictNullChecks": true,
"target": "ESNext"
},
"exclude": ["**/dist", "**/node_modules"],
"include": ["templates"]
}
1 change: 1 addition & 0 deletions shared/tsconfig.eslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"../**/.eslintrc.js",
"../apps",
"../packages",
"../recipes",
"../scripts",
"../shared",
"../test",
Expand Down

0 comments on commit 685ff98

Please sign in to comment.