Repository navigation
EmailRealizer de
🌐 Diese Seite auf: English · Português · Español · Français · Deutsch
Seit 0.2.0-preview.45
Status: Der Kern wurde in 0.2.0-preview.45 AUSGELIEFERT (gemergt am 2026-08-25).
EmailRealizerundEmailRendererexistieren ineQuantic.UI.Emailmit dem Vokabular unten. Noch vor uns: die Matrix echter Clients (ein erzeugtes Beispiel gegen Gmail, Outlook und Apple Mail) und die HTML-Pins, sobald die Ausgabe sich gesetzt hat.
Schreiben Sie die E-Mail in C# mit denselben Komponenten, mit denen eine Seite geschrieben wird, und bekommen Sie HTML zurück, das ein E-Mail-Client tatsächlich rendern wird.
Eine Komponente ist hier ein VisualNode-Baum, der nichts von seinem Ziel weiß. Der Web-Realisierer
macht daraus DOM und CSS; Photon macht daraus GPU-Malen. E-Mail ist ein dritter Realisierer: ein
Ziel, dessen Render-Engine eben sehr restriktiv ist, was architektonisch dieselbe Form hat wie ein
Ziel ganz ohne DOM.
An der Schreibschicht ändert sich nichts. Dieselben Column, Text, Image und Theme-Tokens,
dasselbe Build(context).
Zwei Teile liegen heute im Baum, durch Lesen verifiziert:
-
WebRealizer.Lower(node, theme, typeScale, styles: null): Ohne Style-Senke bleiben die Styles inline am Element, statt zu deduplizierten atomaren Klassen zu werden. Inline-Style ist die härteste Beschränkung der E-Mail, und dieser Weg erzeugt ihn schon. -
HtmlRenderer.RenderNode(...)macht aus dem abgesenkten Baum einen HTML-String.
Also sind "C#-Komponente → HTML mit Inline-Styles" zwei Aufrufe, die es schon gibt. Was fehlt, ist alles, was mit dem Medium zu tun hat.
| Verlangen | Warum | Worin der Web-Realisierer abweicht |
|---|---|---|
Layout aus geschachtelten <table>
|
Outlook unter Windows rendert mit der Engine von Word: kein Flexbox, kein Grid |
Row/Column senken sich zu FlexDirection ab |
Nur style="" inline |
<style>-Blöcke und externes CSS werden von mehreren Clients entfernt oder ignoriert |
Atomare Klassen + ein Stylesheet (der Weg mit Senke) |
| Wörtliche Farben | CSS-Custom-Properties werden nicht aufgelöst | Tokens geben var(--eq-color-…, fallback) aus |
| Absolute Bild-URLs oder CID | Gmail verwirft data:-URIs |
Assets setzen eine ausgelieferte Origin voraus |
| Eine beschränkte Breite | ~600px ist die Konvention, die jeder Client duldet | Seiten sind fließend |
| Keine Interaktion | Kein JS, bei den meisten Clients kein Hover, keine scrollenden Container |
Pressable, ScrollView und Hover-Zustände existieren |
Ein neues Paket, eQuantic.UI.Email, das nur von Primitives und Core abhängt, nie von
eQuantic.UI.Web, damit die beiden Realisierer nicht ineinander abdriften können.
// Genau wie eine Seite geschrieben.
public sealed class WelcomeEmail(string name) : StatelessComponent
{
public override VisualNode Build(ComponentContext context) =>
Column(gap: Space.S4, children: [
Image(Assets.Logo, width: 132),
Text($"Welcome, {name}", TypeRole.Heading),
Button("Confirm your address", href: "https://…"),
]);
}
// Dort gerendert, wo sie gesendet wird.
EmailMessage message = EmailRenderer.Render(new WelcomeEmail("Edgar"), theme);
// message.Html zu Tabellen abgesenkt, vollständig inline
// message.PlainText aus DEMSELBEN Baum erzeugt, nicht zweimal geschriebenEmailMessage trägt beide Teile, weil eine Multipart-Nachricht beide braucht, und die
Text-Alternative aus dem Baum zu erzeugen ist die einzige Art, wie sie mit dem HTML im Takt bleibt.
M0: messen, bevor gebaut wird. Fertig, und sie hat den Plan geändert. Neben den zwei erwarteten
Befunden (das Flex-Layout, die light-dark()-Farben) ein dritter, den der Plan nicht hatte: Die
Typografie lebt in eq-type-*-Klassen, und eine E-Mail hat kein Stylesheet, also rendete jeder
Text in einer Größe. M2 wuchs, um die Rampe des Themes an jeden Text inline zu schreiben. Die
verbleibende Hälfte von M0 ist menschlich: das erzeugte Beispiel gegen echte Postfächer.
M1: zu Tabellen absenken. Fertig. Box, Row, Column und Gap werden geschachtelte
<table>/<tr>/<td> mit Zellen-Padding und Abstandszeilen. Die Ausrichtung bildet sich auf
align/valign ab. Das ist der Hauptteil der Spur.
M2: Inline-Styles und wörtliches Theming. Fertig (dazu die inline geschriebene Typo-Rampe). Nutzt den Weg ohne Senke wieder; löst jedes Token in eine wörtliche Farbe und Länge auf, damit nichts von Custom-Properties abhängt. Dichte und Typskala lösen sich einmal auf, beim Rendern.
M3: Dokumenthülle. Fertig. EmailDocument: Doctype, der <meta>-Satz, der 600px-Container,
die unsichtbare Preheader-Zeile und das Dark-Mode-Meta, das Clients beachten.
M4: Zaun des Vokabulars. Fertig. Diagnosen, die benennen, was eine E-Mail nicht kann:
ScrollView, Pressable, Draggable, das Styling von Hover und Fokus, position: absolute. Ein
Link wird ein <a>; ein Button wird ein kugelsicheres <a>, als Button gestylt. Ein Image
ohne absolute URL ist ein Fehler, kein kaputtes Bild im Postfach von jemandem.
M5: Alternative in reinem Text. Fertig. Denselben Baum für den text/plain-Teil abgehen.
Die Scheibe ging durch fünf Runden Durchsicht (32 Befunde), und was überlebte, sind Regeln, die es wert sind, gesagt zu werden:
-
Eine Eigenschaft, die das Medium nicht ausdrücken kann, wird eingezäunt, nicht angenähert:
Gradienten-Tinte und
MaxLineswerfen und benennen den Ausweg, denn ein Vollton an ihrer Stelle ist eine andere Tinte, die niemand gewählt hat, und mehr Text zu zeigen, als die Autorin begrenzt hat, ist eine Abweichung im Inhalt. -
Eine Eigenschaft, die das Medium anders ausdrückt, wird geehrt: das Padding des Containers (eine
Hülltabelle mit einer Zelle),
Crossund dasAlignSelfje Kind (Zellenausrichtung), Radien je Ecke, Ränder, Überschriftenelemente, dasaria-labelausLink.Label. -
MainAlignist hier gehaltlos, dokumentiert statt eingezäunt: Eine E-Mail-Tabelle bemisst sich nach ihrem Inhalt, es gibt also keinen freien Raum zu verteilen, und somit rendert jeder Wert gleich, was treu ist. -
Eine kaputte Komponente lässt das SENDEN scheitern. Komponenten entfalten sich über
Build, nicht über dasBuildContaineddes Webs: Eine beschreibende Box ist richtig auf einer lebenden Seite, die ein Entwickler ansieht, und falsch in einer Nachricht, die kurz davor ist, das Postfach einer Leserin als Inhalt getarnt zu erreichen. Seit 0.2.0-preview.55 lässt ein Zyklus es genauso scheitern: Der Gang teilt die Tiefenzählung der Naht überComponentBoundary.Enter, und jenseits der Grenze wirft er eineInvalidOperationException, statt einzufassen. Vorher war eine Komponente, die sich selbst baute, eineStackOverflowException, die nichts fangen kann, und der sendende Prozess starb, ohne etwas zu melden. -
Adressen werden geparst, nicht auf Präfixe geprüft; Attributwerte werden als Attribute
kodiert; ein
Linkum verlinkte Runs weigert sich, Anker zu schachteln, und benennt die Entscheidung.
-
Nie ein
<style>-Block, auch dort nicht, wo ein Client ihn unterstützt. Eine Regel schlägt eine Matrix von Ausnahmen. - Keine Interaktivität. Eine E-Mail ist eine gedruckte Seite, die eben Links hat.
- Keine clientspezifischen Tricks in den Komponenten. Was Outlook braucht, lebt im Realisierer, nie in etwas, das eine Autorin schreibt.
- Anhänge und Zustellung gehören nicht uns. Die Ausgabe ist HTML plus Text; das Senden ist die Aufgabe der Anwendung (MailKit, SES, was sie schon nutzt).
Die beiden gleichen Netze, die der Rest des Frameworks nutzt:
- HTML-Pins, mit Absicht neu erzeugt wie die transpilierten Fixtures, sodass eine Layout-Änderung als Diff sichtbar wird statt als Überraschung.
- Eine Client-Matrix als Konformität, gegen echte Clients ausgeführt und festgehalten: Die Regel, dass der Browser der Schiedsrichter ist, gilt hier noch härter, weil E-Mail-Clients weiter von einer Spezifikation entfernt sind als Browser.
🏁 Hier starten
🏗️ Architektur
- Architekturüberblick
- Write-once-Komponenten
- Deklarative Oberfläche
- Paketarchitektur
- Komponenten
- Styling
- Lokalisierung
- Analytics und GTM
📱 Write-once
- Photon-Engine
- Design System
- Fähigkeiten
- Speicher
- Formulare
- Code-Editor
- Markdown
- Mermaid
- E-Mail-Rendering
⚙️ Kompilierung
⚡ Runtime
🔌 Server
🎨 Ökosystem
🚀 Entwicklung