Expand description
Branded transactional mail for Cratefield ventures: one email-client-safe layout, themed with the venture’s own website colours, logo and fonts, and a plain-text twin for every message.
Two parts:
MailTheme— brand name, wordmark, hosted logo and alt text, light and darkPalettes, font stacks, footer lines. Modules get it from the venture’s composition (themed_templates(&theme)on each mail-sending module), the deployment’sMAIL_THEMEconfig (merged on top), or — with neither —MailTheme::for_venture, a neutral theme built from the venture’s name, public URL and coreBrand.Message— a builder for one mail: heading, paragraphs, facts, a primary button with its fallback link, a code value, notes and the footer.Message::renderreturns anEmailwith the HTML and the text part.
use cratefield_mail_templates::{MailTheme, Message};
let theme = MailTheme::new("Acme", "https://acme.test");
let email = Message::new("Sign in to Acme", "Sign in to Acme")
.preheader("This link works once, for 15 minutes.")
.paragraph("Use the button below to sign in.")
.button("Sign in", "https://api.acme.test/v1/auth-magic-link/consume?token=abc")
.fallback_link()
.note("If you didn't ask for this, ignore this email.")
.recipient("ada@example.com")
.why("someone asked to sign in to Acme with this address")
.render(&theme);
assert!(email.html.contains("consume?token=abc"));
assert!(email.text.contains("consume?token=abc"));Email-client safety. Table layout, every style inline (the single
<style> block only adds the narrow-screen and prefers-color-scheme: dark overrides, which clients without <style> support skip), no web
fonts, no tracking pixel, no click tracking, lang and dir on the
document. The only image is the theme’s hosted logo, with alt text; the
layout reads the same without it.
Escaping. Every value — the builder’s text, the theme’s strings and
anything a person typed — reaches the HTML through escape, and the
text part with control characters removed, so no value can add markup
or forge a line. Theme colours and fonts are checked before they reach a
style attribute, and only https:, http: and mailto: URLs become
links.
Structs§
- One rendered mail.
- Mail
Theme - How a venture’s mail looks: its name and logo, its website’s colours in light and dark, its fonts and its footer.
- Message
- One mail, built up and then rendered with a theme.
- Palette
- One colour scheme. Every value is a CSS hex colour (
#rgb,#rrggbbor#rrggbbaa); anything else is replaced by the neutral default when the mail is rendered, so a theme from config can never inject CSS.
Enums§
- Dir
- Text direction of the mail.
Constants§
- DATA_
THEME - The key under which a module puts the resolved theme into the data a mail template receives, so a venture’s own override template can use it too.
- DATA_
THEME_ OVERRIDE - The key under which a module puts the raw
THEME_CONFIG_KEYobject, so a template that carries a composed theme still applies the deployment’s adjustments on top of it. - THEME_
CONFIG_ KEY - The config key a deployment sets to adjust the theme without a rebuild:
a JSON object with any subset of
MailTheme’s fields, merged over the composed theme (nestedlight/darkobjects merge field by field).
Functions§
- attach_
theme - Puts the theme a module resolved from its context into a template’s
data:MailTheme::from_configunderDATA_THEME, and the rawTHEME_CONFIG_KEYobject, when set, underDATA_THEME_OVERRIDE.datathat is not an object is left alone. - escape
- Escapes text for HTML element content and quoted attribute values. Control characters become spaces.
- is_
linkable - Whether
urlmay be a link:https:,http:ormailto:. Anything else (javascript:,data:, a relative path that means nothing in an inbox) is shown as text instead. - plain
- A value for the text part or a header: control characters (a newline in a name, say) become spaces, so no value can forge a line.
- samples
- Representative messages with sample values: what
render-emailswrites for a theme and what the snapshots cover. - theme_
for_ template - The theme a template renders with: the theme it was composed with
(
themed_templates(&theme)in each module) with the deployment’s override applied, else the theme the module attached todata, else the neutral default.