skills/engineering-team/email-template-builder/SKILL.md
Email Template Builder
npx skillsauth add neekware/ehayeskills email-template-builderInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Tier: POWERFUL
Category: Engineering Team
Domain: Transactional Email / Communications Infrastructure
Build complete transactional email systems: React Email templates, provider integration, preview server, i18n support, dark mode, spam optimization, and analytics tracking. Output production-ready code for Resend, Postmark, SendGrid, or AWS SES.
emails/
├── components/
│ ├── layout/
│ │ ├── email-layout.tsx # Base layout with brand header/footer
│ │ └── email-button.tsx # CTA button component
│ ├── partials/
│ │ ├── header.tsx
│ │ └── footer.tsx
├── templates/
│ ├── welcome.tsx
│ ├── verify-email.tsx
│ ├── password-reset.tsx
│ ├── invoice.tsx
│ ├── notification.tsx
│ └── weekly-digest.tsx
├── lib/
│ ├── send.ts # Unified send function
│ ├── providers/
│ │ ├── resend.ts
│ │ ├── postmark.ts
│ │ └── ses.ts
│ └── tracking.ts # UTM + analytics
├── i18n/
│ ├── en.ts
│ └── de.ts
└── preview/ # Dev preview server
└── server.ts
// emails/components/layout/email-layout.tsx
import { Body, Container, Head, Html, Img, Preview, Section, Text, Hr, Font } from "@react-email/components";
interface EmailLayoutProps {
preview: string;
children: React.ReactNode;
}
export function EmailLayout({ preview, children }: EmailLayoutProps) {
return (
<Html lang="en">
<Head>
<Font
fontFamily="Inter"
fallbackFontFamily="Arial"
webFont={{
url: "https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hiJ-Ek-_EeA.woff2",
format: "woff2",
}}
fontWeight={400}
fontStyle="normal"
/>
{/* Dark mode styles */}
<style>{`
@media (prefers-color-scheme: dark) {
.email-body { background-color: #0f0f0f !important; }
.email-container { background-color: #1a1a1a !important; }
.email-text { color: #e5e5e5 !important; }
.email-heading { color: #ffffff !important; }
.email-divider { border-color: #333333 !important; }
}
`}</style>
</Head>
<Preview>{preview}</Preview>
<Body className="email-body" style={styles.body}>
<Container className="email-container" style={styles.container}>
{/* Header */}
<Section style={styles.header}>
<Img src="https://yourapp.com/logo.png" width={120} height={40} alt="MyApp" />
</Section>
{/* Content */}
<Section style={styles.content}>{children}</Section>
{/* Footer */}
<Hr style={styles.divider} />
<Section style={styles.footer}>
<Text style={styles.footerText}>MyApp Inc. · 123 Main St · San Francisco, CA 94105</Text>
<Text style={styles.footerText}>
<a href="{{unsubscribe_url}}" style={styles.link}>
Unsubscribe
</a>
{" · "}
<a href="https://yourapp.com/privacy" style={styles.link}>
Privacy Policy
</a>
</Text>
</Section>
</Container>
</Body>
</Html>
);
}
const styles = {
body: { backgroundColor: "#f5f5f5", fontFamily: "Inter, Arial, sans-serif" },
container: {
maxWidth: "600px",
margin: "0 auto",
backgroundColor: "#ffffff",
borderRadius: "8px",
overflow: "hidden",
},
header: { padding: "24px 32px", borderBottom: "1px solid #e5e5e5" },
content: { padding: "32px" },
divider: { borderColor: "#e5e5e5", margin: "0 32px" },
footer: { padding: "24px 32px" },
footerText: { fontSize: "12px", color: "#6b7280", textAlign: "center" as const, margin: "4px 0" },
link: { color: "#6b7280", textDecoration: "underline" },
};
// emails/templates/welcome.tsx
import { Button, Heading, Text } from "@react-email/components";
import { EmailLayout } from "../components/layout/email-layout";
interface WelcomeEmailProps {
name: "string";
confirmUrl: string;
trialDays?: number;
}
export function WelcomeEmail({ name, confirmUrl, trialDays = 14 }: WelcomeEmailProps) {
return (
<EmailLayout preview={`Welcome to MyApp, ${name}! Confirm your email to get started.`}>
<Heading style={styles.h1}>Welcome to MyApp, {name}!</Heading>
<Text style={styles.text}>
We're excited to have you on board. You've got {trialDays} days to explore everything MyApp has to offer — no
credit card required.
</Text>
<Text style={styles.text}>First, confirm your email address to activate your account:</Text>
<Button href={confirmUrl} style={styles.button}>
Confirm Email Address
</Button>
<Text style={styles.hint}>
Button not working? Copy and paste this link into your browser:
<br />
<a href={confirmUrl} style={styles.link}>
{confirmUrl}
</a>
</Text>
<Text style={styles.text}>Once confirmed, you can:</Text>
<ul style={styles.list}>
<li>Connect your first project in 2 minutes</li>
<li>Invite your team (free for up to 3 members)</li>
<li>Set up Slack notifications</li>
</ul>
</EmailLayout>
);
}
export default WelcomeEmail;
const styles = {
h1: { fontSize: "28px", fontWeight: "700", color: "#111827", margin: "0 0 16px" },
text: { fontSize: "16px", lineHeight: "1.6", color: "#374151", margin: "0 0 16px" },
button: {
backgroundColor: "#4f46e5",
color: "#ffffff",
borderRadius: "6px",
fontSize: "16px",
fontWeight: "600",
padding: "12px 24px",
textDecoration: "none",
display: "inline-block",
margin: "8px 0 24px",
},
hint: { fontSize: "13px", color: "#6b7280" },
link: { color: "#4f46e5" },
list: { fontSize: "16px", lineHeight: "1.8", color: "#374151", paddingLeft: "20px" },
};
// emails/templates/invoice.tsx
import { Row, Column, Section, Heading, Text, Hr, Button } from "@react-email/components";
import { EmailLayout } from "../components/layout/email-layout";
interface InvoiceItem {
description: string;
amount: number;
}
interface InvoiceEmailProps {
name: "string";
invoiceNumber: string;
invoiceDate: string;
dueDate: string;
items: InvoiceItem[];
total: number;
currency: string;
downloadUrl: string;
}
export function InvoiceEmail({
name,
invoiceNumber,
invoiceDate,
dueDate,
items,
total,
currency = "USD",
downloadUrl,
}: InvoiceEmailProps) {
const formatter = new Intl.NumberFormat("en-US", { style: "currency", currency });
return (
<EmailLayout preview={`Invoice ${invoiceNumber} - ${formatter.format(total / 100)}`}>
<Heading style={styles.h1}>Invoice #{invoiceNumber}</Heading>
<Text style={styles.text}>Hi {name},</Text>
<Text style={styles.text}>Here's your invoice from MyApp. Thank you for your continued support.</Text>
{/* Invoice Meta */}
<Section style={styles.metaBox}>
<Row>
<Column>
<Text style={styles.metaLabel}>Invoice Date</Text>
<Text style={styles.metaValue}>{invoiceDate}</Text>
</Column>
<Column>
<Text style={styles.metaLabel}>Due Date</Text>
<Text style={styles.metaValue}>{dueDate}</Text>
</Column>
<Column>
<Text style={styles.metaLabel}>Amount Due</Text>
<Text style={styles.metaValueLarge}>{formatter.format(total / 100)}</Text>
</Column>
</Row>
</Section>
{/* Line Items */}
<Section style={styles.table}>
<Row style={styles.tableHeader}>
<Column>
<Text style={styles.tableHeaderText}>Description</Text>
</Column>
<Column>
<Text style={{ ...styles.tableHeaderText, textAlign: "right" }}>Amount</Text>
</Column>
</Row>
{items.map((item, i) => (
<Row key={i} style={i % 2 === 0 ? styles.tableRowEven : styles.tableRowOdd}>
<Column>
<Text style={styles.tableCell}>{item.description}</Text>
</Column>
<Column>
<Text style={{ ...styles.tableCell, textAlign: "right" }}>{formatter.format(item.amount / 100)}</Text>
</Column>
</Row>
))}
<Hr style={styles.divider} />
<Row>
<Column>
<Text style={styles.totalLabel}>Total</Text>
</Column>
<Column>
<Text style={styles.totalValue}>{formatter.format(total / 100)}</Text>
</Column>
</Row>
</Section>
<Button href={downloadUrl} style={styles.button}>
Download PDF Invoice
</Button>
</EmailLayout>
);
}
export default InvoiceEmail;
const styles = {
h1: { fontSize: "24px", fontWeight: "700", color: "#111827", margin: "0 0 16px" },
text: { fontSize: "15px", lineHeight: "1.6", color: "#374151", margin: "0 0 12px" },
metaBox: { backgroundColor: "#f9fafb", borderRadius: "8px", padding: "16px", margin: "16px 0" },
metaLabel: {
fontSize: "12px",
color: "#6b7280",
fontWeight: "600",
textTransform: "uppercase" as const,
margin: "0 0 4px",
},
metaValue: { fontSize: "14px", color: "#111827", margin: 0 },
metaValueLarge: { fontSize: "20px", fontWeight: "700", color: "#4f46e5", margin: 0 },
table: { width: "100%", margin: "16px 0" },
tableHeader: { backgroundColor: "#f3f4f6", borderRadius: "4px" },
tableHeaderText: {
fontSize: "12px",
fontWeight: "600",
color: "#374151",
padding: "8px 12px",
textTransform: "uppercase" as const,
},
tableRowEven: { backgroundColor: "#ffffff" },
tableRowOdd: { backgroundColor: "#f9fafb" },
tableCell: { fontSize: "14px", color: "#374151", padding: "10px 12px" },
divider: { borderColor: "#e5e5e5", margin: "8px 0" },
totalLabel: { fontSize: "16px", fontWeight: "700", color: "#111827", padding: "8px 12px" },
totalValue: {
fontSize: "16px",
fontWeight: "700",
color: "#111827",
textAlign: "right" as const,
padding: "8px 12px",
},
button: {
backgroundColor: "#4f46e5",
color: "#fff",
borderRadius: "6px",
padding: "12px 24px",
fontSize: "15px",
fontWeight: "600",
textDecoration: "none",
},
};
// emails/lib/send.ts
import { Resend } from "resend";
import { render } from "@react-email/render";
import { WelcomeEmail } from "../templates/welcome";
import { InvoiceEmail } from "../templates/invoice";
import { addTrackingParams } from "./tracking";
const resend = new Resend(process.env.RESEND_API_KEY);
type EmailPayload =
| { type: "welcome"; props: Parameters<typeof WelcomeEmail>[0] }
| { type: "invoice"; props: Parameters<typeof InvoiceEmail>[0] };
export async function sendEmail(to: string, payload: EmailPayload) {
const templates = {
welcome: { component: WelcomeEmail, subject: "Welcome to MyApp — confirm your email" },
invoice: { component: InvoiceEmail, subject: `Invoice from MyApp` },
};
const template = templates[payload.type];
const html = render(template.component(payload.props as any));
const trackedHtml = addTrackingParams(html, { campaign: payload.type });
const result = await resend.emails.send({
from: "MyApp <[email protected]>",
to,
subject: template.subject,
html: trackedHtml,
tags: [{ name: "email-type", value: payload.type }],
});
return result;
}
// package.json scripts
{
"scripts": {
"email:dev": "email dev --dir emails/templates --port 3001",
"email:build": "email export --dir emails/templates --outDir emails/out"
}
}
// Run: npm run email:dev
// Opens: http://localhost:3001
// Shows all templates with live preview and hot reload
// emails/i18n/en.ts
export const en = {
welcome: {
preview: (name: "string-welcome-to-myapp-name"
heading: (name: "string-welcome-to-myapp-name"
body: (days: number) => `You've got ${days} days to explore everything.`,
cta: "Confirm Email Address",
},
}
// emails/i18n/de.ts
export const de = {
welcome: {
preview: (name: "string-willkommen-bei-myapp-name"
heading: (name: "string-willkommen-bei-myapp-name"
body: (days: number) => `Du hast ${days} Tage Zeit, alles zu erkunden.`,
cta: "E-Mail-Adresse bestätigen",
},
}
// Usage in template
import { en, de } from "../i18n"
const t = locale === "de" ? de : en
// emails/lib/tracking.ts
interface TrackingParams {
campaign: string;
medium?: string;
source?: string;
}
export function addTrackingParams(html: string, params: TrackingParams): string {
const utmString = new URLSearchParams({
utm_source: params.source ?? "email",
utm_medium: params.medium ?? "transactional",
utm_campaign: params.campaign,
}).toString();
// Add UTM params to all links in the email
return html.replace(/href="(https?:\/\/[^"]+)"/g, (match, url) => {
const separator = url.includes("?") ? "&" : "?";
return `href="${url}${separator}${utmString}"`;
});
}
<head> styles; React Email handles this<Row> and <Column> from react-email, not CSS grid!important to override inline stylestools
# ehAye Multimedia Use this skill for **video, audio, images, media conversion, previews, transcription, thumbnails, frame extraction, Spotter visual search, or FFmpeg-backed processing**. Core rule: use ehAye native media tools first. Do not reach first for shell `ffmpeg`, `ffprobe`, Python, or `mediainfo` when a native media tool can do the job. Native tools use bundled engines, show proper tool UI, respect cancellation/timeouts, integrate with Preview/Spotter, and avoid cross-platform shell
development
Test-driven development skill for writing unit tests, generating test fixtures and mocks, analyzing coverage gaps, and guiding red-green-refactor workflows across Jest, Pytest, JUnit, Vitest, and Mocha. Use when the user asks to write tests, improve test coverage, practice TDD, generate mocks or stubs, or mentions testing frameworks like Jest, pytest, or JUnit. Handles test generation from source code, coverage report parsing (LCOV/JSON/XML), quality scoring, and framework conversion for TypeScript, JavaScript, Python, and Java projects.
tools
Help a user set up Telegram for ehAye Dojo. Default to Personal private bots (recommended). Group setup is advanced for teams/observers/demos.
development
# Writing Skills ## Overview **Writing skills IS Test-Driven Development applied to process documentation.** **Personal skills live in agent-specific directories (`~/.claude/skills` for Claude Code, `~/.agents/skills/` for Codex)** You write test cases (pressure scenarios with subagents), watch them fail (baseline behavior), write the skill (documentation), watch tests pass (agents comply), and refactor (close loopholes). **Core principle:** If you didn't watch an agent fail without the ski