diff --git a/.changeset/social-icons-png-output.md b/.changeset/social-icons-png-output.md
new file mode 100644
index 00000000..fe8d717b
--- /dev/null
+++ b/.changeset/social-icons-png-output.md
@@ -0,0 +1,6 @@
+---
+"@templatical/renderer": patch
+"@templatical/editor": patch
+---
+
+Render social icons as hosted PNGs instead of inline SVG data URIs so they display in Outlook desktop (the Word rendering engine has no SVG support and rejects base64 in ``). PNGs are shipped with the npm package and served via the version-pinned unpkg URL by default; override via the new `RenderOptions.socialIconsBaseUrl` to self-host. Replace the Style segmented control in the social icons sidebar with a native dropdown so the 5-option list no longer overflows the sidebar.
diff --git a/.gitignore b/.gitignore
index b1dece95..63f4954c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,9 +5,6 @@ coverage
.DS_Store
.idea
*.tsbuildinfo
-apps/playground/.env
-apps/docs/.vitepress/cache
-apps/docs/.vitepress/dist
playwright-report/
test-results/
blob-report/
diff --git a/apps/docs/.gitignore b/apps/docs/.gitignore
new file mode 100644
index 00000000..7b64d98e
--- /dev/null
+++ b/apps/docs/.gitignore
@@ -0,0 +1,2 @@
+.vitepress/cache
+.vitepress/dist
\ No newline at end of file
diff --git a/apps/docs/api/renderer-typescript.md b/apps/docs/api/renderer-typescript.md
index c02ad8d4..600cc9b1 100644
--- a/apps/docs/api/renderer-typescript.md
+++ b/apps/docs/api/renderer-typescript.md
@@ -40,6 +40,7 @@ interface RenderOptions {
defaultFallbackFont?: string;
allowHtmlBlocks?: boolean; // default: true
renderCustomBlock?: (block: CustomBlock) => Promise;
+ socialIconsBaseUrl?: string;
}
```
@@ -49,6 +50,7 @@ interface RenderOptions {
| `defaultFallbackFont` | `'Arial, sans-serif'` | Fallback font stack |
| `allowHtmlBlocks` | `true` | Set to `false` to strip HTML blocks from output |
| `renderCustomBlock` | -- | Resolves custom blocks to HTML. Called once per custom block. Editor consumers pass `editor.renderCustomBlock`; headless consumers wire their own resolver. If omitted, custom blocks fall back to the block's `renderedHtml` field (if present) and otherwise are omitted. |
+| `socialIconsBaseUrl` | version-pinned unpkg URL | Base URL (no trailing slash) for the social icon PNG assets. Resolved per icon to `${baseUrl}/${style}/${platform}.png`. See [Social icons](#social-icons) below. |
### Custom blocks
@@ -77,6 +79,34 @@ const mjml = await renderToMjml(content, {
});
```
+### Social icons
+
+Social icon blocks are emitted as ``. The default `socialIconsBaseUrl` points at the version-pinned unpkg mirror of `@templatical/renderer`, which ships pre-rasterized PNGs (16 platforms × 5 styles) alongside the package:
+
+```
+https://unpkg.com/@templatical/renderer@/assets/social/{style}/{platform}.png
+```
+
+**Why PNGs.** Outlook desktop (Word rendering engine) does not support SVG and rejects base64 data URIs in ``. Hosted PNGs are the only format that renders across every mainstream email client.
+
+**Why version-pinned.** Email is archival — recipients open messages months or years after they're sent. The version pin freezes the icon visuals at render time so a future redesign or regression in the package doesn't retroactively break already-delivered emails. It also avoids a per-image 302 redirect and unlocks long-lived immutable cache headers.
+
+**Self-hosting.** Override `socialIconsBaseUrl` to serve the assets from your own CDN — useful for air-gapped environments, brand-specific theming, or removing the unpkg dependency:
+
+```ts
+const mjml = await renderToMjml(content, {
+ socialIconsBaseUrl: 'https://cdn.example.com/email-assets/social',
+});
+```
+
+The exact filenames the renderer expects are `{style}/{platform}.png` where `style` is one of `solid | outlined | rounded | square | circle` and `platform` is one of `facebook | twitter | instagram | linkedin | youtube | tiktok | pinterest | email | whatsapp | telegram | discord | snapchat | reddit | github | dribbble | behance`. The shipped 192×192 PNGs are a reasonable starting point if you want to mirror them.
+
+The package also exports `DEFAULT_SOCIAL_ICONS_BASE_URL` if you want to compose URLs against the same default:
+
+```ts
+import { DEFAULT_SOCIAL_ICONS_BASE_URL } from '@templatical/renderer';
+```
+
## Utilities
The renderer also exports utility functions:
@@ -88,13 +118,12 @@ import {
convertMergeTagsToValues,
isHiddenOnAll,
toPaddingString,
- generateSocialIconDataUri,
renderBlock,
getCssClassAttr,
getCssClasses,
getWidthPercentages,
getWidthPixels,
- SOCIAL_ICONS,
+ DEFAULT_SOCIAL_ICONS_BASE_URL,
RenderContext,
} from '@templatical/renderer';
```
@@ -154,15 +183,6 @@ toPaddingString({ top: 10, right: 20, bottom: 10, left: 20 });
// '10px 20px 10px 20px'
```
-### `generateSocialIconDataUri(platform, style, size)`
-
-Generates a base64-encoded SVG data URI for a social media platform icon. Used internally by the renderer for social icon blocks:
-
-```ts
-const uri = generateSocialIconDataUri('twitter', 'circle', 32);
-// 'data:image/svg+xml,...'
-```
-
### `renderBlock(block, context)`
Renders a single block to its MJML representation. Used internally by `renderToMjml()` but exported for advanced use cases where you need to render individual blocks.
@@ -179,9 +199,9 @@ Generate CSS class attributes from a block's visibility settings. Used internall
Calculate column widths for a given `ColumnLayout`. Returns an array of percentage or pixel values per column.
-### `SOCIAL_ICONS`
+### `DEFAULT_SOCIAL_ICONS_BASE_URL`
-A map of all built-in social platform SVG icon data, keyed by platform and style.
+The default value of `RenderOptions.socialIconsBaseUrl` — the version-pinned unpkg URL pointing at this package's bundled social icon PNGs. See [Social icons](#social-icons).
## Compiling MJML to HTML
diff --git a/apps/docs/de/api/renderer-typescript.md b/apps/docs/de/api/renderer-typescript.md
index 6fa37772..16d3ec6f 100644
--- a/apps/docs/de/api/renderer-typescript.md
+++ b/apps/docs/de/api/renderer-typescript.md
@@ -40,6 +40,7 @@ interface RenderOptions {
defaultFallbackFont?: string;
allowHtmlBlocks?: boolean; // Standard: true
renderCustomBlock?: (block: CustomBlock) => Promise;
+ socialIconsBaseUrl?: string;
}
```
@@ -49,6 +50,7 @@ interface RenderOptions {
| `defaultFallbackFont` | `'Arial, sans-serif'` | Fallback-Schriftart-Stack |
| `allowHtmlBlocks` | `true` | Auf `false` setzen, um HTML-Blöcke aus der Ausgabe zu entfernen |
| `renderCustomBlock` | -- | Wandelt benutzerdefinierte Blöcke in HTML um. Wird einmal pro benutzerdefiniertem Block aufgerufen. Editor-Konsumenten übergeben `editor.renderCustomBlock`; Headless-Konsumenten verwenden einen eigenen Resolver. Wenn weggelassen, fällt der Renderer auf das `renderedHtml`-Feld des Blocks zurück (falls vorhanden) und lässt den Block andernfalls weg. |
+| `socialIconsBaseUrl` | versionsgebundene unpkg-URL | Basis-URL (ohne abschließenden Schrägstrich) für die PNG-Assets der Social-Media-Icons. Wird pro Icon zu `${baseUrl}/${style}/${platform}.png` aufgelöst. Siehe [Social-Media-Icons](#social-media-icons) unten. |
### Benutzerdefinierte Blöcke
@@ -77,6 +79,34 @@ const mjml = await renderToMjml(content, {
});
```
+### Social-Media-Icons
+
+Social-Icon-Blöcke werden als `` ausgegeben. Der Standardwert von `socialIconsBaseUrl` verweist auf den versionsgebundenen unpkg-Mirror von `@templatical/renderer`, der vorgerasterte PNGs (16 Plattformen × 5 Stile) mit dem Paket ausliefert:
+
+```
+https://unpkg.com/@templatical/renderer@/assets/social/{style}/{platform}.png
+```
+
+**Warum PNGs.** Outlook Desktop (Word-Rendering-Engine) unterstützt kein SVG und lehnt base64-Daten-URIs in `` ab. Gehostete PNGs sind das einzige Format, das in allen gängigen E-Mail-Clients zuverlässig dargestellt wird.
+
+**Warum versionsgebunden.** E-Mails sind Archivinhalt — Empfänger öffnen Nachrichten Monate oder Jahre nach dem Versand. Die Versionsbindung friert die Icon-Darstellung zum Renderzeitpunkt ein, sodass ein späteres Redesign oder ein Regressionsfehler im Paket bereits zugestellte E-Mails nicht rückwirkend beschädigt. Außerdem entfällt eine 302-Weiterleitung pro Icon und es können langlebige, unveränderliche Cache-Header gesetzt werden.
+
+**Selbst hosten.** Überschreiben Sie `socialIconsBaseUrl`, um die Assets über Ihr eigenes CDN auszuliefern — nützlich für Air-Gapped-Umgebungen, markenspezifische Themen oder um die Abhängigkeit von unpkg zu entfernen:
+
+```ts
+const mjml = await renderToMjml(content, {
+ socialIconsBaseUrl: 'https://cdn.example.com/email-assets/social',
+});
+```
+
+Die exakten Dateinamen, die der Renderer erwartet, sind `{style}/{platform}.png`, wobei `style` einer von `solid | outlined | rounded | square | circle` und `platform` einer von `facebook | twitter | instagram | linkedin | youtube | tiktok | pinterest | email | whatsapp | telegram | discord | snapchat | reddit | github | dribbble | behance` ist. Die ausgelieferten 192×192-PNGs sind ein sinnvoller Ausgangspunkt, wenn Sie sie spiegeln möchten.
+
+Das Paket exportiert außerdem `DEFAULT_SOCIAL_ICONS_BASE_URL`, falls Sie URLs gegen denselben Standardwert komponieren möchten:
+
+```ts
+import { DEFAULT_SOCIAL_ICONS_BASE_URL } from '@templatical/renderer';
+```
+
## Hilfsfunktionen
Der Renderer exportiert außerdem Hilfsfunktionen:
@@ -88,13 +118,12 @@ import {
convertMergeTagsToValues,
isHiddenOnAll,
toPaddingString,
- generateSocialIconDataUri,
renderBlock,
getCssClassAttr,
getCssClasses,
getWidthPercentages,
getWidthPixels,
- SOCIAL_ICONS,
+ DEFAULT_SOCIAL_ICONS_BASE_URL,
RenderContext,
} from '@templatical/renderer';
```
@@ -154,15 +183,6 @@ toPaddingString({ top: 10, right: 20, bottom: 10, left: 20 });
// '10px 20px 10px 20px'
```
-### `generateSocialIconDataUri(platform, style, size)`
-
-Erzeugt eine base64-kodierte SVG-Data-URI für ein Social-Media-Plattform-Icon. Wird intern vom Renderer für Social-Icon-Blöcke verwendet:
-
-```ts
-const uri = generateSocialIconDataUri('twitter', 'circle', 32);
-// 'data:image/svg+xml,...'
-```
-
### `renderBlock(block, context)`
Rendert einen einzelnen Block in seine MJML-Darstellung. Wird intern von `renderToMjml()` verwendet, aber für fortgeschrittene Anwendungsfälle exportiert, in denen Sie einzelne Blöcke rendern müssen.
@@ -179,9 +199,9 @@ Erzeugen CSS-Klassen-Attribute aus den Sichtbarkeitseinstellungen eines Blocks.
Berechnen Spaltenbreiten für ein gegebenes `ColumnLayout`. Gibt ein Array von Prozent- oder Pixelwerten pro Spalte zurück.
-### `SOCIAL_ICONS`
+### `DEFAULT_SOCIAL_ICONS_BASE_URL`
-Eine Zuordnung aller eingebauten SVG-Icon-Daten für soziale Plattformen, nach Plattform und Stil indiziert.
+Der Standardwert von `RenderOptions.socialIconsBaseUrl` — die versionsgebundene unpkg-URL, die auf die in diesem Paket mitgelieferten PNGs der Social-Media-Icons verweist. Siehe [Social-Media-Icons](#social-media-icons).
## MJML zu HTML kompilieren
diff --git a/apps/playground/.gitignore b/apps/playground/.gitignore
new file mode 100644
index 00000000..2eea525d
--- /dev/null
+++ b/apps/playground/.gitignore
@@ -0,0 +1 @@
+.env
\ No newline at end of file
diff --git a/packages/editor/src/components/toolbar/SocialToolbar.vue b/packages/editor/src/components/toolbar/SocialToolbar.vue
index 5317e4c3..a166bd43 100644
--- a/packages/editor/src/components/toolbar/SocialToolbar.vue
+++ b/packages/editor/src/components/toolbar/SocialToolbar.vue
@@ -17,6 +17,7 @@ import {
import type {
SocialIcon,
SocialIconsBlock,
+ SocialIconStyle,
SocialPlatform,
} from "@templatical/types";
import { generateId } from "@templatical/types";
@@ -74,6 +75,7 @@ function removeSocialIcon(iconId: string): void {
>