DevactionUI.Email (devaction_ui v0.7.0)

Copy Markdown View Source

The frame and the text of an e-mail: the document with its <head>, the 600 px card with the brand above and the footer below, the heading, the paragraph, the button and the copy-and-paste link.

An e-mail client has no Tailwind, no CSS variables and no flexbox: everything here is a table with inline styles, and the hex values are the canvas's own, kept in one place (a private style module). The only stylesheet is the <style> of document/1: the 600 px column, the narrow padding under 620 px and the dark mode, which the client turns on with prefers-color-scheme and which restyles the elements that carry an em-* class (em-body, em-card, em-ink, em-muted, em-soft, em-line, em-panel, em-btn-primary, em-btn-secondary).

Every text is yours: the components draw no words of their own, so one e-mail can be written in any language. The sibling modules draw the rest: DevactionUI.EmailTag, DevactionUI.EmailCallout and DevactionUI.EmailTable.

document/1 wraps layout/1, and to_html/1 turns the result into the string a mailer sends:

<.document lang="en" title="Sign in">
  <.layout preheader="..." brand_name="Acme" brand_href="https://acme.test" logo_src="https://acme.test/logo.png" signature="Acme Inc.">
    <.heading>Sign in</.heading>
  </.layout>
</.document>

Summary

Functions

The button of the e-mail: a table cell with a link in it, 12 px radius, which every client draws the same.

Two or three buttons side by side, each a little narrower than a single button/1.

A person and what they did: a round avatar with initials and a sentence beside it, above the heading.

The small mono caption above a block (a list, a table): 11 px, spaced, in the soft color. Write it in capitals.

A thin rule between the body and the fine print.

The whole HTML document of an e-mail: doctype, <head> with the viewport and color-scheme metas and the one <style> the e-mail needs, and a <body> with the page background.

A strong run of text inside a paragraph, in the ink color. On the card it turns light in dark mode with the rest of the ink; inside a tinted block, say surface={:tint} so it stays dark.

An underlined link for the footer paragraph: preferences, unsubscribe.

The title of the e-mail: 26 px, 600 weight, in the ink color.

The page of an e-mail: the hidden preheader, the brand, the white card and the footer, in a 600 px column.

The address to copy when the button does not work: a line of text and the URL in mono, breaking anywhere.

A paragraph of the e-mail.

The <style> of an e-mail: the 600 px column, the narrow padding and the dark rules. document/1 includes it; a preview inside another page can place it by itself, with a dark_scope.

Renders a ~H result (or the output of a function component) to the HTML string a mailer sends.

Functions

button(assigns)

@spec button(map()) :: Phoenix.LiveView.Rendered.t()

The button of the e-mail: a table cell with a link in it, 12 px radius, which every client draws the same.

:primary is the ink button (it turns light in dark mode), :secondary the outlined one.

Example

<.button href={@url}>Sign in</.button>
<.button href={@reset_url} variant={:secondary}>Reset password</.button>

Attributes

  • href (:string) (required)
  • variant (:atom) - Defaults to :primary. Must be one of :primary, or :secondary.
  • Global attributes are accepted. goes on the table that holds the button.

Slots

  • inner_block (required)

button_row(assigns)

@spec button_row(map()) :: Phoenix.LiveView.Rendered.t()

Two or three buttons side by side, each a little narrower than a single button/1.

Example

<.button_row>
  <:button href={@draft_url}>Draft the renewal</:button>
  <:button href={@url} variant={:secondary}>Open contract</:button>
</.button_row>

Attributes

  • Global attributes are accepted.

Slots

  • button (required) - a button of the row. Accepts attributes:
    • href (:string) (required)
    • variant (:atom) - Must be one of :primary, or :secondary.

byline(assigns)

@spec byline(map()) :: Phoenix.LiveView.Rendered.t()

A person and what they did: a round avatar with initials and a sentence beside it, above the heading.

Example

<.byline initials="CA">
  <.emphasis>Carlos</.emphasis> invited you to <.emphasis>Acme</.emphasis>.
</.byline>

Attributes

  • initials (:string) (required) - the letters in the round avatar.
  • Global attributes are accepted.

Slots

  • inner_block (required) - the sentence beside the avatar.

caption(assigns)

@spec caption(map()) :: Phoenix.LiveView.Rendered.t()

The small mono caption above a block (a list, a table): 11 px, spaced, in the soft color. Write it in capitals.

Example

<.caption>WHAT TO LOOK AT</.caption>

Attributes

  • Global attributes are accepted.

Slots

  • inner_block (required)

divider(assigns)

@spec divider(map()) :: Phoenix.LiveView.Rendered.t()

A thin rule between the body and the fine print.

Example

<.divider />

Attributes

  • Global attributes are accepted.

document(assigns)

@spec document(map()) :: Phoenix.LiveView.Rendered.t()

The whole HTML document of an e-mail: doctype, <head> with the viewport and color-scheme metas and the one <style> the e-mail needs, and a <body> with the page background.

Example

<.document lang="en" title="Your link">
  <.layout ...>...</.layout>
</.document>

Attributes

  • lang (:string) (required) - the language of the e-mail, as in en or pt-BR.
  • title (:string) (required) - the <title>, usually the subject.
  • dark_scope (:string) - a CSS selector ([data-theme=dark]) that turns the dark rules on instead of the prefers-color-scheme query; for previews. Defaults to nil.
  • Global attributes are accepted. goes on the <body>.

Slots

  • inner_block (required)

emphasis(assigns)

@spec emphasis(map()) :: Phoenix.LiveView.Rendered.t()

A strong run of text inside a paragraph, in the ink color. On the card it turns light in dark mode with the rest of the ink; inside a tinted block, say surface={:tint} so it stays dark.

Example

<.paragraph>The link works for <.emphasis>15 minutes</.emphasis>.</.paragraph>

Attributes

  • surface (:atom) - :tint inside a callout, whose light background stays light in dark mode and so must keep the dark ink. Defaults to :card. Must be one of :card, or :tint.
  • Global attributes are accepted.

Slots

  • inner_block (required)

heading(assigns)

@spec heading(map()) :: Phoenix.LiveView.Rendered.t()

The title of the e-mail: 26 px, 600 weight, in the ink color.

Example

<.heading>Sign in to Acme</.heading>

Attributes

  • Global attributes are accepted.

Slots

  • inner_block (required)

layout(assigns)

@spec layout(map()) :: Phoenix.LiveView.Rendered.t()

The page of an e-mail: the hidden preheader, the brand, the white card and the footer, in a 600 px column.

Each footer slot is one paragraph; put a footer_link/1 in it for preferences or unsubscribe.

Example

<.layout
  preheader="Click to sign in."
  brand_name="Acme"
  brand_href="https://acme.test"
  logo_src="https://acme.test/logo.png"
  signature="Acme Inc. · Springfield"
>
  <.heading>Sign in</.heading>
  <:footer>You got this because you have an account.</:footer>
</.layout>

Attributes

  • preheader (:string) (required) - the line a mail list shows after the subject; hidden in the body.
  • brand_name (:string) (required) - the name beside the logo.
  • brand_href (:string) (required) - where the logo and the name lead.
  • logo_src (:string) (required) - an absolute URL of the logo, a square image.
  • logo_size (:integer) - the logo's side in px. Defaults to 36.
  • signature (:string) (required) - the last footer line: the company and where it is.
  • Global attributes are accepted.

Slots

  • inner_block (required) - the content of the card.
  • footer - a paragraph of the footer above the signature; give one per line (why the e-mail came, links).

paragraph(assigns)

@spec paragraph(map()) :: Phoenix.LiveView.Rendered.t()

A paragraph of the e-mail.

Example

<.paragraph>Click the button. The link works <.emphasis>once</.emphasis>.</.paragraph>
<.paragraph size={:small} flush>If it was not you, ignore this e-mail.</.paragraph>

Attributes

  • size (:atom) - :body 16 px, :compact 15 px, :small 13 px in the soft color for the fine print. Defaults to :body. Must be one of :body, :compact, or :small.
  • lead (:boolean) - the ink color, for the greeting line. Defaults to false.
  • flush (:boolean) - no margin below, for the last paragraph. Defaults to false.
  • Global attributes are accepted.

Slots

  • inner_block (required)

styles(assigns)

@spec styles(map()) :: Phoenix.LiveView.Rendered.t()

The <style> of an e-mail: the 600 px column, the narrow padding and the dark rules. document/1 includes it; a preview inside another page can place it by itself, with a dark_scope.

Example

<.styles />

Attributes

  • dark_scope (:string) - a CSS selector that turns the dark rules on instead of the prefers-color-scheme query. Defaults to nil.
  • Global attributes are accepted. goes on the <style>.

to_html(rendered)

@spec to_html(Phoenix.LiveView.Rendered.t()) :: String.t()

Renders a ~H result (or the output of a function component) to the HTML string a mailer sends.

Example

html = DevactionUI.Email.to_html(~H"<.heading>Hello</.heading>")