Osy#betaRun it on Osyrin: osyrin.com

Reference / UI

The EmailTemplates kit — mail that looks designed

EmailLayout("Acme", preview: …, address: …) { EmailHeading(…); EmailText(…); EmailTable() { EmailRow(…); EmailTotal(…); } EmailButton(…, …); }

`Osysharp.EmailTemplates` ships the parts a designed mail is built from — a layout with its preview line, logo and a footer carrying the sender's address and an unsubscribe link, a headline, paragraphs, a button, a picture, a code to copy, and a receipt's lines and total. Each is an `[Email]` part in the app's own theme, so a mail written with them looks like the rest of the product. And the MAIL BLOCKS staff arrange in a mail's region — words, a heading, a picture, a button, a quote, small print.

stable1 example compiled by CIuiemailkit

Source on GitHub: Osysharp.EmailTemplates — github.com/osysharp/emailtemplates ↗

Summary#

A mail that looks designed has the same few pieces every time: the sender's name at the top, a clear headline, a paragraph or two, the lines of a receipt with their amounts, one obvious button, and a footer saying who sent it. This kit is those pieces, written as [[ui-email|[Email]]] parts in the app's own theme.

// app.osy
use Osysharp.EmailTemplates@0;

Signature#

EmailLayout(string brand, string? preview = null, string? logo = null, string? address = null,
            string? unsubscribe = null, string? footer = null) { … }
EmailHeading(string text)
EmailText(string text)
EmailNote(string text)
EmailButton(string label, string href)
EmailImage(string src, string alt, int width = 552, bool inline = false)
EmailCode(string code)
EmailDivider()
EmailTable() { … }                                   // EmailRow + EmailTotal inside
EmailRow(string label, string value, string? detail = null)
EmailTotal(string label, string value)

// The mail blocks — [Block] [Email] parts staff place in a mail's Region:
EmailWords(Markdown words)
EmailHeadline(string heading, Markdown words = "")
EmailPicture(Picture picture, string alt = "")
EmailAction(string label, PageLink link = "/")
EmailQuote(string quote, string who = "", Color ground = Colors.Muted)
EmailSmallPrint(Markdown words)

Description#

The frame: EmailLayout#

EmailLayout is the outside of every mail: the sender's own page — the theme's background and its type — in a 600-pixel column, the width every client lays out well, with the brand at the top, the mail's content under it, and a footer below a rule. A mail reads like one of the app's pages, not like one of its cards: a shop that is black type on white sends black type on white, whatever colour its panels are.

  • brand is the sender's name. It heads the mail unless there is a logo, which replaces it. A logo that is a file the app stores (public/logo.png) travels INSIDE the mail, so the brand shows even where a client blocks remote pictures; see [[ui-email#pictures]].
  • preview is the line an inbox shows beside the subject. Without one, a client shows the mail's first words.
  • address is where the sender is. A marketing mail must carry a postal address, and a receipt is better for one.
  • unsubscribe is the address that stops this kind of mail. Give it for anything a reader signed up to, and never for a mail they cannot stop receiving, such as a receipt.
  • footer is one more line of small print above the address, such as why the reader is getting this mail.

The parts that go inside#

partwhat it draws
EmailHeading(text)the headline, in the theme's display face
EmailText(text)a paragraph — a line break in the text is kept
EmailNote(text)small print in the muted colour
EmailButton(label, href)the one thing to do next — a button that is a link in every client, Outlook included
EmailImage(src, alt, width, inline)a picture, 552 pixels wide unless told otherwise (the column's inner width) — by address, or with inline: true a file the app stores, carried inside the mail
EmailCode(code)a code to type or read out — a gift code, a return code — large and spaced
EmailDivider()a rule between two parts of the mail
EmailTable() { … }a receipt's lines, under a rule — EmailRows and an EmailTotal
EmailRow(label, value, detail)one line: what it is, with an optional detail under it (a size), and what it costs
EmailTotal(label, value)the line a receipt ends on, bold, under a rule

It wears your theme#

Every colour, size, corner and face in the kit is a theme token: Colors.Primary for the button, Colors.Bg and Colors.OnBg for the page and its type, FontSize.Title for the headline, Font.Display for its face. The renderer writes the app's value for each, so an app that declares theme Brand { Colors { Primary = "#C2410C"; } } sends orange buttons without touching the kit.

A part of your own#

Each part is an ordinary [Email] component over the platform's email atoms, so a different one is written the same way. osy get EmailButton copies the kit's into your app as a starting point.

The mail blocks — what staff arrange#

A mail's words that staff own are a [[ui-email#region|region]] of blocks, arranged at the desk like a page's. The kit ships the everyday ones; each is an [Email] part marked [Block], in the app's theme, its words written with placeholders: true so they are filled from whichever mail they are placed in:

blockwhat staff fill in
EmailWords — Wordsa paragraph in Markdown — what the mail says first
EmailHeadline — Heading and wordsa heading, and a paragraph under it
EmailPicture — Picturea photograph across the mail — one the app ships, or one uploaded — and what it shows
EmailAction — Buttonwhat it says, and where it goes (a page of the app, or an address)
EmailQuote — Quotewhat somebody said, who said it, and one of the theme's grounds to set it on
EmailSmallPrint — Small printa quiet line at the foot of the mail
Region("welcome", allow: [EmailWords, EmailHeadline, EmailPicture, EmailAction, EmailQuote, EmailSmallPrint]) {
  EmailWords(words: "Welcome aboard, **{member.name}**.");
}

A region needs use Osysharp.Blocks; in the app — that is where what staff arrange is kept. An app that arranges nothing uses the same components as ordinary parts, and needs nothing more: EmailQuote("…", who: "…") in a template is a quote.

A save of words naming a placeholder the mail does not fill is refused by name (Osysharp.Blocks' WordsProblem), and PlaceholdersIn, UnknownPlaceholders and PlaceholderRefusal — the same check over any words, a campaign's body say — are there too.

Examples#

A receipt, from the order it confirms#

app EmailKitExample {
  use Osysharp.EmailTemplates;
  model "model/**/*.osy";
}

using Osysharp.EmailTemplates;

entity Sale {
  [Required, MaxLength(40)] string Reference;
  [Required, MaxLength(120)] string Customer;
  [ForeignKey(Sale)] SaleLine[] Lines;
  security { allow read, create when IsAuthenticated; }
}

entity SaleLine {
  [Required] Sale Sale;
  [Required, MaxLength(120)] string Name;
  [MaxLength(60)] string? Size;
  int Quantity;
  decimal Price;
  security { allow read, create when IsAuthenticated; }
}

string Kronor(decimal amount) { return $"{amount:0.00} kr"; }

[Email]
component SaleReceipt(Sale sale) {
  subject => $"Your receipt {sale.Reference}";
  render {
    EmailLayout("Grounds & Grapes", preview: $"Thank you, {sale.Customer} — receipt {sale.Reference}",
                address: "Storgatan 1, 111 22 Stockholm") {
      EmailHeading($"Thank you, {sale.Customer}");
      EmailText("Your order is reserved and on its way to being packed.");
      EmailTable() {
        foreach (var line in sale.Lines) {
          EmailRow($"{line.Quantity} × {line.Name}", Kronor(line.Price * line.Quantity), detail: line.Size);
        }
        EmailTotal("Total", Kronor(sale.Lines.Sum(l => l.Price * l.Quantity)));
      }
      EmailButton("See your order", $"https://shop.example.com/orders/{sale.Reference}");
      EmailNote("Questions? Reply to this mail.");
    }
  }
}

See also#

Related

[Email] — mail rendered from data

An `[Email]` component is a mail template. It is rendered on the server — from any function, a workflow step included —…

Blocks and regions — pages the staff arrange

A `[Block]` is a component staff can place on a page: its parameters are the fields they fill in, and each parameter's…

The PageBuilder kit — the desk staff arrange the site at

`Osysharp.PageBuilder` is the desk where the people who run a site arrange its [[ui-blocks|regions]]: add, move, fill…

theme tokens

A `theme` block names your app's design tokens — colors, spacing, radii, and more — as reusable values. A token can…