What
- Add
mailer.layout (a path) and mailer.partials (a { header, footer, button, styles } map of paths). Devkit ships the defaults in config/templates/_layout/, built on brand tokens: logo, colors, font, signature, contact footer and links.
render(key, params, { subject }) compiles the body, then the layout. The layout receives {{{body}}}, brand and subject, and subject fills <title>.
- Use an isolated
handlebars.create() instance, with partials registered from config.
- Legacy passthrough: a template whose trimmed source starts with
<!doctype or <html (case-insensitive) is sent unwrapped.
- Layout and partial paths go through the resolver's containment check.
Acceptance criteria
Tests
- Wrap vs passthrough, including a leading-whitespace or uppercase doctype.
- A partial override from a mocked downstream-shaped config.
- Containment on layout and partial paths.
- Escaping: params stay escaped and the body is injected once.
Depends on: resolver and brand.
What
mailer.layout(a path) andmailer.partials(a{ header, footer, button, styles }map of paths). Devkit ships the defaults inconfig/templates/_layout/, built on brand tokens: logo, colors, font, signature, contact footer and links.render(key, params, { subject })compiles the body, then the layout. The layout receives{{{body}}},brandandsubject, andsubjectfills<title>.handlebars.create()instance, with partials registered from config.<!doctypeor<html(case-insensitive) is sent unwrapped.Acceptance criteria
{{> button url=url label="..."}}is available to every template.Tests
Depends on: resolver and brand.