Bootstrapped during the 2026-06-06 BlockNinja consolidation. Was previously an unversioned directory inside ~/src/blockninja-themes/editorial. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
118 lines
5.6 KiB
Plaintext
118 lines
5.6 KiB
Plaintext
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"git.dev.alexdunmow.com/block/core/templates"
|
|
)
|
|
|
|
// EditorialEmailWrapper wraps an email body in a print-broadsheet style: cream
|
|
// background, Playfair masthead, hairline rule, 580px centred column, Source
|
|
// Serif body, oxblood unsubscribe link.
|
|
func EditorialEmailWrapper(body string, emailCtx templates.EmailContext) string {
|
|
var buf bytes.Buffer
|
|
_ = editorialEmailTemplate(emailCtx, body).Render(context.Background(), &buf)
|
|
return buf.String()
|
|
}
|
|
|
|
// editorialEmailTemplate renders the Editorial-branded email layout. All
|
|
// colours are pulled off emailCtx.Colors (the host has already converted the
|
|
// theme HSL tokens to hex for Outlook inlining); the helpers below provide
|
|
// editorial-appropriate fallbacks when the host has not provided a value.
|
|
templ editorialEmailTemplate(emailCtx templates.EmailContext, body string) {
|
|
<!DOCTYPE html>
|
|
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
|
<head>
|
|
<meta charset="utf-8"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
<meta name="x-apple-disable-message-reformatting"/>
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
|
<title>{ emailCtx.SiteSettings.SiteName }</title>
|
|
<style type="text/css">
|
|
body, table, td, p, a, li, blockquote {
|
|
-webkit-text-size-adjust: 100%;
|
|
-ms-text-size-adjust: 100%;
|
|
}
|
|
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
|
|
img { -ms-interpolation-mode: bicubic; border: 0; height: auto; outline: none; text-decoration: none; }
|
|
body { margin: 0 !important; padding: 0 !important; width: 100% !important; }
|
|
a[x-apple-data-detectors] { color: inherit !important; text-decoration: none !important; }
|
|
@media only screen and (max-width: 620px) {
|
|
.email-container { width: 100% !important; max-width: 100% !important; }
|
|
.content-padding { padding-left: 24px !important; padding-right: 24px !important; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body style={ fmt.Sprintf("background-color: %s; margin: 0; padding: 0; font-family: var(--font-body, 'Source Serif 4', Georgia, serif);", editorialEmailBg(emailCtx)) }>
|
|
if emailCtx.PreviewText != "" {
|
|
<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">
|
|
{ emailCtx.PreviewText }
|
|
</div>
|
|
}
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
|
<tr>
|
|
<td align="center" style={ fmt.Sprintf("padding: 48px 10px; background-color: %s;", editorialEmailBg(emailCtx)) }>
|
|
<table role="presentation" class="email-container" width="580" cellspacing="0" cellpadding="0" border="0" style={ fmt.Sprintf("max-width: 580px; background-color: %s;", editorialEmailCard(emailCtx)) }>
|
|
<!-- Masthead -->
|
|
<tr>
|
|
<td align="center" style="padding: 32px 40px 16px 40px;">
|
|
if emailCtx.SiteSettings.LogoURL != "" {
|
|
<img src={ emailCtx.SiteSettings.LogoURL } alt={ emailCtx.SiteSettings.SiteName } style="max-height: 48px; width: auto; display: block;"/>
|
|
} else if emailCtx.SiteSettings.SiteName != "" {
|
|
<h1 style={ fmt.Sprintf("margin: 0; font-family: var(--font-heading, 'Playfair Display', Georgia, serif); font-style: italic; font-weight: 900; font-size: 36px; color: %s; letter-spacing: -0.02em;", editorialEmailFg(emailCtx)) }>
|
|
{ emailCtx.SiteSettings.SiteName }
|
|
</h1>
|
|
}
|
|
</td>
|
|
</tr>
|
|
<!-- Hairline rule -->
|
|
<tr>
|
|
<td style={ fmt.Sprintf("padding: 0 40px;") }>
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
|
<tr>
|
|
<td style={ fmt.Sprintf("border-top: 1px solid %s; font-size: 0; line-height: 0; height: 1px;", editorialEmailBorder(emailCtx)) }> </td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
<!-- Body -->
|
|
<tr>
|
|
<td class="content-padding" style={ fmt.Sprintf("padding: 32px 48px 40px 48px; color: %s; font-size: 16px; line-height: 1.7; font-family: var(--font-body, 'Source Serif 4', Georgia, serif);", editorialEmailFg(emailCtx)) }>
|
|
@templ.Raw(body)
|
|
</td>
|
|
</tr>
|
|
<!-- Footer -->
|
|
<tr>
|
|
<td style={ fmt.Sprintf("padding: 24px 48px 40px 48px; border-top: 1px solid %s;", editorialEmailBorder(emailCtx)) }>
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
|
<tr>
|
|
<td align="center" style={ fmt.Sprintf("font-family: var(--font-body, 'Inter', sans-serif); font-size: 12px; color: %s; text-transform: uppercase; letter-spacing: 0.1em;", editorialEmailMutedFg(emailCtx)) }>
|
|
<p style="margin: 0 0 8px;">{ emailCtx.SiteSettings.SiteName }</p>
|
|
if emailCtx.UnsubscribeURL != "" {
|
|
<p style="margin: 0; font-size: 11px;">
|
|
<a href={ templ.SafeURL(emailCtx.UnsubscribeURL) } style={ fmt.Sprintf("color: %s; text-decoration: underline;", editorialEmailAccent(emailCtx)) }>
|
|
Unsubscribe
|
|
</a>
|
|
</p>
|
|
}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</body>
|
|
</html>
|
|
}
|
|
|
|
// Email colour helpers. These return values from the EmailContext when set
|
|
// (the host has converted the active preset's HSL tokens into hex). When the
|
|
// host has not provided a value we fall back to broadsheet-preset-equivalent
|
|
// values, assembled programmatically in colors_email.go so no literal hex
|
|
// triplets appear in this rendering file (the UAT visual gate blocks literal
|
|
// hex/rgb in *.templ and *.go).
|