# `PhoenixKit.Email.Markdown`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.55.1/lib/phoenix_kit/email/markdown.ex#L1)

An email body written in Markdown, as HTML and as plain text.

`PhoenixKit.Email.Content.resolve/5` uses this for a `markdown` part
(`markdown[.<locale>].md`): the HTML body comes from it when the message has
no `html` part, and the text body when it has no `text` part.

## Placeholders

`{{variable}}` and `{{{variable}}}` work as in an `html` part: two braces
escape the value, three insert it raw. They are substituted **after** the
Markdown is rendered — a renderer percent-encodes `{{url}}` in a link
target, so a value substituted before it would arrive as `%7B%7Burl%7D%7D`,
and a value rendered as Markdown could change the markup around it. Each
placeholder is swapped for an opaque token, the Markdown is rendered and
sanitized (`PhoenixKit.Utils.HtmlSanitizer`), the placeholders are put back
and substituted with `PhoenixKit.Templates.Substitution`.

## Links and buttons

Every link and image is built here rather than by the renderer, so its
address is known after substitution:

  * The address is the link target with its placeholders filled in, raw or
    not — inside an attribute both spellings are escaped as an attribute
    value.
  * Only an `http://`, `https://` or `mailto:` address (images: `http(s)`
    only) is kept. Anything else — `javascript:`, a relative path, a
    placeholder nothing bound — drops the link and keeps its label as text.
  * A paragraph that is exactly one `[label](url)` link becomes a button: a
    table cell in the accent colour, which renders the same in every email
    client. A bare address on its own line stays a link.
  * Other links are coloured with the accent colour.

The accent colour is the `accent_color` variable, checked by
`PhoenixKit.Email.Branding.normalize_color/1`.

## A paragraph that is only placeholders

A top-level paragraph of plain text whose placeholders all fill in blank —
`{{failed_attempts}}` on its own line, with nothing to report — is left
out of both bodies, rather than sent as an empty paragraph. A value that
is a sentence ending in a blank line, written as its own paragraph, stays
its own paragraph.

A paragraph that is exactly one `{{{variable}}}` is the value alone, with
no `<p>` around it — the way to place a block of HTML a caller built (a
table of invoice lines) between Markdown paragraphs. The text body
substitutes it as is, so a caller that passes HTML that way gives the
email a `text` part of its own.

## Raw HTML

HTML written inside the Markdown is not rendered: a placeholder inside an
attribute of it could not be checked like a link target. A body that needs
markup of its own belongs in an `html` part.

# `to_html`

```elixir
@spec to_html(String.t(), PhoenixKit.Templates.Substitution.variables()) :: String.t()
```

`markdown` as a sanitized HTML fragment with `variables` substituted.

# `to_text`

```elixir
@spec to_text(String.t(), PhoenixKit.Templates.Substitution.variables()) :: String.t()
```

`markdown` as plain text with `variables` substituted.

Headings and paragraphs are lines separated by a blank line, list items
start with `- ` (`1. ` when numbered), `[label](url)` becomes
`label: url` — just `url` when the label is the address itself, as in
`[{{url}}]({{url}})` — an image becomes its alt text, and emphasis, code
and raw HTML marks are dropped. A value that ends in blank lines never
leaves more than one blank line in a row.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
