Skip to content

EmailRealizer pt BR

Edgar Mesquita edited this page Aug 24, 2026 · 2 revisions

Renderização de Email (Track M)

🌐 Esta página em: English · Português

Estado: planejado, não construído. Nada abaixo existe hoje. Esta página é o desenho e as fatias; a entrada do roadmap aponta para cá.

Escrever o email em C# com os mesmos componentes com que se escreve uma página, e receber de volta um HTML que um cliente de email realmente renderiza.

Por que isso encaixa na arquitetura em vez de brigar com ela

Um componente aqui é uma árvore de VisualNode que não sabe nada sobre seu alvo. O realizador web a transforma em DOM e CSS; o Photon a transforma em pintura na GPU. Email é um terceiro realizador — um alvo cujo motor de render é muito restritivo, o que é arquiteturalmente a mesma forma de um alvo sem DOM nenhum.

Nada na camada de autoria muda. Os mesmos Column, Text, Image e tokens de tema, o mesmo Build(context).

O que já existe

Duas peças estão na árvore hoje, verificadas lendo o código:

  • WebRealizer.Lower(node, theme, typeScale, styles: null) — sem o sink de estilos, os estilos ficam inline no elemento em vez de virarem classes atômicas deduplicadas. Estilo inline é a restrição mais dura do email, e esse caminho já o produz.
  • HtmlRenderer.RenderNode(...) transforma a árvore rebaixada numa string de HTML.

Ou seja, "componente C# → HTML com estilos inline" são duas chamadas que já existem. O que falta é tudo o que diz respeito ao meio.

O que o email realmente exige

Exigência Por quê Onde o realizador web difere
Layout em <table> aninhada O Outlook no Windows renderiza com o motor do Word: sem flexbox, sem grid Row/Column descem para FlexDirection
style="" inline Blocos <style> e CSS externo são removidos ou ignorados por vários clientes Classes atômicas + folha de estilo (o caminho do sink)
Cores literais Custom properties do CSS não são resolvidas Tokens emitem var(--eq-color-…, fallback)
URL absoluta de imagem ou CID URIs data: são descartadas pelo Gmail Assets assumem uma origem servida
Largura restrita ~600px é a convenção que todo cliente tolera Páginas são fluidas
Nenhuma interação Sem JS, sem hover na maioria dos clientes, sem contêiner rolável Pressable, ScrollView e estados de hover existem

Desenho

Um pacote novo, eQuantic.UI.Email, dependendo apenas de Primitives e Core — nunca de eQuantic.UI.Web, para que os dois realizadores não possam derivar um para dentro do outro.

// Escrito exatamente como uma página.
public sealed class WelcomeEmail(string name) : StatelessComponent
{
    public override VisualNode Build(ComponentContext context) =>
        Column(gap: Space.S4, children: [
            Image(Assets.Logo, width: 132),
            Text($"Bem-vindo, {name}", TypeRole.Heading),
            Button("Confirme seu endereço", href: "https://…"),
        ]);
}

// Renderizado onde é enviado.
EmailMessage message = EmailRenderer.Render(new WelcomeEmail("Edgar"), theme);
// message.Html          — rebaixado para tabelas, totalmente inline
// message.PlainText     — gerado da MESMA árvore, não escrito duas vezes

EmailMessage carrega as duas partes porque uma mensagem multipart precisa das duas, e gerar a alternativa em texto a partir da árvore é o único jeito de ela ficar em dia com o HTML.

Fatias

M0 — Medir antes de construir. Pegar um componente que já existe, renderizar pelas duas chamadas acima, e abrir o resultado no Gmail (web, iOS, Android), no Outlook (desktop Windows, web) e no Apple Mail. Produzir uma tabela do que sobrevive intacto. Isso decide o tamanho da M1, e é meio dia. Nenhum código novo.

M1 — Rebaixar para tabelas. Box, Row, Column e Gap viram <table>/<tr>/<td> aninhados, com padding de célula e linhas espaçadoras. Alinhamento mapeia para align/valign. É o grosso do track.

M2 — Estilos inline e tema literal. Reusar o caminho sem sink; resolver todo token para uma cor e um comprimento literais, para nada depender de custom properties. Densidade e escala de tipo resolvem uma vez, na renderização.

M3 — Casca do documento. EmailDocument: doctype, o conjunto de <meta>, o contêiner de 600px, a linha invisível de preheader, e o meta de modo escuro que os clientes honram.

M4 — Cerca do vocabulário. Diagnósticos nomeando o que um email não faz — ScrollView, Pressable, Draggable, estilo de hover e foco, position: absolute. Um Link vira <a>; um Button vira um <a> à prova de balas estilizado como botão. Uma Image sem URL absoluta é um erro, não uma figura quebrada na caixa de entrada de alguém.

M5 — Alternativa em texto puro. Percorrer a mesma árvore para a parte text/plain.

Cercas

  • Nunca um bloco <style>, mesmo onde um cliente o suporte. Uma regra vence uma matriz de exceções.
  • Sem interatividade. Um email é uma página impressa que por acaso tem links.
  • Sem gambiarra por cliente dentro dos componentes. O que o Outlook precisa mora no realizador, nunca em algo que um autor escreve.
  • Anexos e envio não são nossos. A saída é HTML mais texto; enviar é trabalho da app (MailKit, SES, o que ela já usa).

Como se testa

As mesmas duas redes que o resto do framework usa:

  • Pins de HTML, regenerados de propósito como as fixtures transpiladas, para que uma mudança de layout apareça como diff e não como surpresa.
  • Uma matriz de clientes como conformidade, rodada contra clientes reais e registrada — a regra do "o browser é o árbitro" vale aqui ainda mais forte, porque clientes de email estão mais longe de uma especificação do que browsers estão.

Clone this wiki locally