Files
petfeed/src/lib/mail-template.ts
T
adminandClaude Sonnet 5 fd6f1f0564 fix(mail): use inline paw SVG instead of emoji in branded header (Gitea #21)
The 🐾 emoji rendered inconsistently across mail clients/OS fonts and
looked off-brand. Replaces it with the exact lucide-react "paw-print"
mark (same path data as the landing page's filled paw icon) inlined
as raw SVG in the email header.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 15:27:04 +02:00

123 lines
5.1 KiB
TypeScript

import "server-only";
/**
* Branded HTML shell for transactional emails, mirroring the landing page's
* orange gradient hero + paw-print wordmark (src/app/page.tsx) so PawFeed
* mail doesn't look like unstyled plaintext dumped into an HTML wrapper
* (Gitea report: "html plain" emails with a bare "Hallo," greeting).
*
* Table-based layout with inline styles throughout — email clients (Outlook
* especially) ignore most <style> block CSS and flexbox/grid entirely, so
* this deliberately avoids Tailwind classes and modern layout properties.
*/
interface BrandedMailInput {
previewText: string;
greetingName?: string | null;
bodyHtml: string;
ctaLabel?: string;
ctaUrl?: string;
}
const ORANGE_500 = "#f97316";
const ORANGE_600 = "#ea580c";
const ORANGE_50 = "#fff7ed";
const TEXT_MUTED = "#78716c";
const TEXT_BODY = "#292524";
/**
* Same mark as the landing page's filled paw icon (lucide-react "paw-print",
* fill="currentColor" strokeWidth={0} — see src/app/page.tsx navbar/hero).
* Inlined as raw SVG rather than an emoji so the header matches the brand
* mark exactly instead of relying on the recipient's OS emoji font.
*/
const PAW_MARK_SVG = `<svg width="28" height="28" viewBox="0 0 24 24" fill="#ffffff" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="PawFeed">
<circle cx="11" cy="4" r="2" />
<circle cx="18" cy="8" r="2" />
<circle cx="20" cy="16" r="2" />
<path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z" />
</svg>`;
export function buildGreeting(firstName?: string | null): string {
return firstName ? `Hallo ${firstName},` : "Hallo,";
}
export function renderBrandedMailHtml({
previewText,
greetingName,
bodyHtml,
ctaLabel,
ctaUrl,
}: BrandedMailInput): string {
const greeting = buildGreeting(greetingName);
const cta =
ctaLabel && ctaUrl
? `
<tr>
<td style="padding: 8px 40px 32px;">
<table role="presentation" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="border-radius: 10px; background-color: ${ORANGE_500};">
<a href="${ctaUrl}" style="display: inline-block; padding: 13px 28px; font-family: 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 15px; font-weight: 600; color: #ffffff; text-decoration: none;">
${ctaLabel}
</a>
</td>
</tr>
</table>
</td>
</tr>`
: "";
return `<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>PawFeed</title>
</head>
<body style="margin: 0; padding: 0; background-color: #f5f5f4;">
<div style="display: none; max-height: 0; overflow: hidden; opacity: 0;">${previewText}</div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color: #f5f5f4; padding: 32px 16px;">
<tr>
<td align="center">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0" style="max-width: 600px; width: 100%; background-color: #ffffff; border-radius: 20px; overflow: hidden;">
<tr>
<td style="background-color: ${ORANGE_500}; padding: 36px 40px;" align="center">
<table role="presentation" cellpadding="0" cellspacing="0" border="0" align="center" style="margin: 0 auto;">
<tr>
<td style="vertical-align: middle; padding-right: 10px; line-height: 0;">
${PAW_MARK_SVG}
</td>
<td style="vertical-align: middle;">
<span style="font-family: 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 26px; font-weight: 800; color: #ffffff; letter-spacing: -0.02em;">PawFeed</span>
</td>
</tr>
</table>
<div style="font-family: 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 500; color: ${ORANGE_50}; margin-top: 6px;">
Where pets are the stars.
</div>
</td>
</tr>
<tr>
<td style="padding: 36px 40px 8px; font-family: 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.6; color: ${TEXT_BODY};">
<p style="margin: 0 0 16px;">${greeting}</p>
${bodyHtml}
</td>
</tr>
${cta}
<tr>
<td style="padding: 24px 40px 32px; border-top: 1px solid #f0efed;">
<p style="margin: 0; font-family: 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 13px; line-height: 1.6; color: ${TEXT_MUTED};">
Dein PawFeed-Team<br />
Diese E-Mail wurde automatisch von <a href="https://pawfeed.org" style="color: ${ORANGE_600}; text-decoration: none;">pawfeed.org</a> versendet.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>`;
}