eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
START HERE
Getting Started
WRITE-ONCE
Write-Once Components
Declarative Surface
Photon Engine
Design System
Capabilities
Storage
Forms
Code Editor
Markdown
Mermaid
Email Rendering
ARCHITECTURE
Architecture Overview
Package Architecture
Components
Styling
Localization
Analytics & GTM
COMPILATION
Compiler
Compile-Time Evaluation
Supported C# Features
External Type Resolution
Build Flow
Diagnostics
RUNTIME
Runtime (TypeScript)
Performance
SERVER
Server Integration
Assets
BunPackage
Security
ECOSYSTEM
Image
Charts
Icons
Lottie
DEVELOPMENT
Visual Editor
Debug
Roadmap
PT-BR
Analytics-pt-BR
Architecture-pt-BR
Assets-pt-BR
BuildFlow-pt-BR
BunPackage-pt-BR
Capabilities-pt-BR
Charts-pt-BR
CodeEditor-pt-BR
Compiler-pt-BR
CompileTimeEvaluation-pt-BR
Components-pt-BR
Debug-pt-BR
DeclarativeSurface-pt-BR
DesignSystem-pt-BR
Diagnostics-pt-BR
EmailRealizer-pt-BR
ExternalTypeResolution-pt-BR
Forms-pt-BR
GettingStarted-pt-BR
Home-pt-BR
Icons-pt-BR
Image-pt-BR
Localization-pt-BR
Lottie-pt-BR
Markdown-pt-BR
Mermaid-pt-BR
PackageArchitecture-pt-BR
Performance-pt-BR
Photon-pt-BR
Roadmap-pt-BR
Runtime-pt-BR
Security-pt-BR
ServerIntegration-pt-BR
Storage-pt-BR
Styling-pt-BR
SupportedFeatures-pt-BR
VisualEditor-pt-BR
WriteOnceComponents-pt-BR
ACTIONS & INPUTS
Button
IconButton
TextInput
Select
Checkbox
Switch
RadioGroup
SegmentedControl
Slider
Stepper
SearchField
SURFACES & DISPLAY
Card
Badge
Chip
Avatar
Banner
ProgressBar
EmptyState
Divider
NAVIGATION
Tabs
AppBar
BottomNavigation
Breadcrumb
Pagination
PageIndicator
Menu
Drawer
OVERLAYS
Dialog
BottomSheet
Toast
Popover
Tooltip
LISTS & DATA
List
ListView
Table
Accordion
CodeBlock
TOUCH INTERACTION
PullToRefresh
SwipeableRow
Docspt-BR
Renderização de Email (Track M)
Edit this page
6 min read
🌐 Esta página em: English · Português
Since 0.2.0-preview.45
ESTADO: O NÚCLEO SAIU NA 0.2.0-PREVIEW.45 (merge em 2026-08-25). EmailRealizer e EmailRenderer existem em eQuantic.UI.Email com o vocabulário abaixo. Ainda à frente: a matriz de clientes reais (um sample gerado contra Gmail/Outlook/Apple Mail) e os pins de HTML quando a saída assentar.
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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. Feita — e mudou o plano. Além dos dois achados esperados (layout flex, cores light-dark()), um terceiro que o plano não tinha: a tipografia vive nas classes eq-type-*, e um email não tem folha de estilos, então todo texto saía num tamanho só. A M2 cresceu para inlinar a rampa do tema em cada texto. A metade restante da M0 é humana: o sample gerado contra caixas de correio reais.
M1 — Rebaixar para tabelas. Feita. 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. Feita (mais a rampa de tipo inlinada). 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. Feita. 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. Feita. 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. Feita. Percorrer a mesma árvore para a parte text/plain.
O que a revisão ensinou ao contrato
A fatia passou por cinco rondas de revisão (32 achados) e o que sobreviveu são regras que valem ser ditas:
Propriedade que o meio não expressa é cercada, não aproximada: tinta em gradiente e MaxLines atiram nomeando a saída — um sólido no lugar é outra tinta que ninguém escolheu, e mostrar mais texto do que o autor limitou é divergência de conteúdo.
Propriedade que o meio expressa diferente é honrada: padding de contêiner (tabela-envelope de uma célula), Cross e AlignSelf por filho (alinhamento da célula), raios por canto, bordas, elementos de heading, aria-label do Link.Label.
MainAlign é vácuo aqui, documentado em vez de cercado: uma tabela de email dimensiona-se ao conteúdo, então não há espaço livre a distribuir — todo valor renderiza igual, o que é fiel.
Um componente quebrado falha o ENVIO. Componentes expandem via Build, não o BuildContained do web: uma caixa de erro é certa numa página viva que um developer está olhando e errada numa mensagem prestes a chegar à caixa de entrada de um leitor vestida de conteúdo.
Endereços são parseados, não checados por prefixo; valores de atributo são codificados; um Link em volta de runs com link recusa aninhar âncoras, nomeando a escolha.
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.