131 lines
2.9 KiB
TypeScript
131 lines
2.9 KiB
TypeScript
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from "@react-email/components";
|
|
import type { ReactNode } from "react";
|
|
|
|
const brand = "#4338ca";
|
|
const ink = "#18181b";
|
|
const muted = "#71717a";
|
|
const border = "#e4e4e7";
|
|
const surface = "#fafafa";
|
|
|
|
interface EmailLayoutProps {
|
|
/** Short preview line shown in inbox lists. */
|
|
preview: string;
|
|
/** App display name shown in the header/footer. */
|
|
appName: string;
|
|
children: ReactNode;
|
|
}
|
|
|
|
/**
|
|
* Shared shell for transactional email. Neutral, system-font, inline-styled
|
|
* (no Tailwind) so the Worker bundle stays lean and rendering is fast.
|
|
*/
|
|
export function EmailLayout({ preview, appName, children }: EmailLayoutProps) {
|
|
return (
|
|
<Html lang="en" dir="ltr">
|
|
<Head />
|
|
<Preview>{preview}</Preview>
|
|
<Body style={layoutBodyStyle}>
|
|
<Container style={containerStyle}>
|
|
<Section style={headerStyle}>
|
|
<Text style={brandStyle}>{appName}</Text>
|
|
</Section>
|
|
|
|
<Section style={cardStyle}>{children}</Section>
|
|
|
|
<Hr style={hrStyle} />
|
|
<Text style={footerStyle}>
|
|
You received this email because you have an account or were invited to {appName}. If
|
|
this wasn't you, you can safely ignore it.
|
|
</Text>
|
|
</Container>
|
|
</Body>
|
|
</Html>
|
|
);
|
|
}
|
|
|
|
const layoutBodyStyle: React.CSSProperties = {
|
|
backgroundColor: surface,
|
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
|
margin: 0,
|
|
padding: "24px 0",
|
|
};
|
|
|
|
const containerStyle: React.CSSProperties = {
|
|
maxWidth: 480,
|
|
margin: "0 auto",
|
|
padding: "0 16px",
|
|
};
|
|
|
|
const headerStyle: React.CSSProperties = {
|
|
padding: "8px 0 16px",
|
|
};
|
|
|
|
const brandStyle: React.CSSProperties = {
|
|
color: brand,
|
|
fontSize: 18,
|
|
fontWeight: 700,
|
|
letterSpacing: "-0.01em",
|
|
margin: 0,
|
|
};
|
|
|
|
const cardStyle: React.CSSProperties = {
|
|
backgroundColor: "#ffffff",
|
|
border: `1px solid ${border}`,
|
|
borderRadius: 12,
|
|
padding: "28px 28px 24px",
|
|
};
|
|
|
|
const hrStyle: React.CSSProperties = {
|
|
border: "none",
|
|
borderTop: `1px solid ${border}`,
|
|
margin: "24px 0 16px",
|
|
};
|
|
|
|
const footerStyle: React.CSSProperties = {
|
|
color: muted,
|
|
fontSize: 12,
|
|
lineHeight: "18px",
|
|
margin: 0,
|
|
textAlign: "center",
|
|
};
|
|
|
|
/**
|
|
* Shared content styles used by the email templates (heading, body text,
|
|
* primary button, muted helper text).
|
|
*/
|
|
export const titleStyle: React.CSSProperties = {
|
|
color: ink,
|
|
fontSize: 22,
|
|
fontWeight: 700,
|
|
letterSpacing: "-0.01em",
|
|
margin: "0 0 12px",
|
|
};
|
|
|
|
export const bodyStyle: React.CSSProperties = {
|
|
color: ink,
|
|
fontSize: 14,
|
|
lineHeight: "22px",
|
|
margin: "0 0 12px",
|
|
};
|
|
|
|
export const buttonStyle: React.CSSProperties = {
|
|
backgroundColor: brand,
|
|
borderRadius: 8,
|
|
color: "#ffffff",
|
|
display: "inline-block",
|
|
fontSize: 14,
|
|
fontWeight: 600,
|
|
margin: "16px 0",
|
|
padding: "10px 18px",
|
|
textDecoration: "none",
|
|
};
|
|
|
|
export const mutedStyle: React.CSSProperties = {
|
|
color: muted,
|
|
fontSize: 12,
|
|
lineHeight: "18px",
|
|
margin: "16px 0 0",
|
|
};
|
|
|
|
export const emailTokens = { brand, ink, muted, border } as const;
|