From 11c070caf6a6ed58e7c982db207b3c06ab3ef9ee Mon Sep 17 00:00:00 2001 From: Zulfiqar Ali Date: Mon, 12 Oct 2026 20:29:08 -0400 Subject: [PATCH 1/4] feat: social icons take a tone, separate from their shape iconStyle mixed shape with how color is applied, and a one-color icon had no place in it. SocialIconsBlock gains an optional iconTone ("brand", the default and what an absent value means, or "dark" or "light"), and iconStyle gains "plain", the glyph alone. The editor shows them as Style and Color. It's iconTone rather than iconColor because every *Color field in the block model holds a CSS color, and the editor's palette check reads them that way. The colors, glyph size and PNG folder names live in @templatical/types, shared by the editor's inline SVG, the renderer and the PNG build, which now writes every style in every tone. Brand-colored icons keep their URLs and bytes. The MJML importer reads a tone back from its folder, and Topol's outlinedbw set imports as outlined in dark. The docs say to mirror the {style}-{tone} folders when self-hosting, and recommend a filled shape for dark icons, since dark-mode clients darken the background but not images. Co-Authored-By: Claude Opus 5.5 --- .changeset/social-icon-tone.md | 17 ++++ apps/docs/api/editor.md | 2 +- apps/docs/api/renderer-typescript.md | 8 +- apps/docs/api/types.md | 5 +- apps/docs/de/api/editor.md | 2 +- apps/docs/de/api/renderer-typescript.md | 8 +- apps/docs/de/api/types.md | 5 +- apps/docs/de/guide/blocks.md | 5 +- apps/docs/guide/blocks.md | 5 +- apps/docs/public/llms-full.txt | 20 ++-- .../src/components/blocks/SocialIconSvg.vue | 31 +++--- .../components/blocks/SocialIconsBlock.vue | 1 + .../src/components/toolbar/SocialToolbar.vue | 20 ++++ packages/editor/src/i18n/locales/ca.ts | 5 + packages/editor/src/i18n/locales/de.ts | 5 + packages/editor/src/i18n/locales/en.ts | 5 + packages/editor/src/i18n/locales/es.ts | 5 + packages/editor/src/i18n/locales/fr.ts | 5 + packages/editor/src/i18n/locales/ja.ts | 5 + packages/editor/src/i18n/locales/nl.ts | 5 + packages/editor/src/i18n/locales/pt-BR.ts | 5 + packages/editor/tests/socialIconTone.test.ts | 96 +++++++++++++++++++ packages/editor/tests/socialToolbar.test.ts | 27 ++++++ .../src/__tests__/composite-mapper.test.ts | 18 ++++ .../src/__tests__/round-trip.test.ts | 1 + packages/import-mjml/src/composite-mapper.ts | 37 ++++--- .../src/__tests__/social-mapper.test.ts | 5 +- packages/import-topol/src/social-mapper.ts | 28 +++--- .../renderer/scripts/rasterize-social.mjs | 71 ++++++++------ packages/renderer/src/renderers/social.ts | 11 ++- packages/renderer/tests/social.test.ts | 49 +++++++--- packages/template-tools/schema.json | 19 +++- packages/types/src/blocks.ts | 13 ++- packages/types/src/index.ts | 11 ++- packages/types/src/social.ts | 82 +++++++++++++++- packages/types/tests/social.test.ts | 87 ++++++++++++++++- skills/templatical/reference/block-guide.md | 7 +- skills/templatical/reference/schema.json | 19 +++- .../tests/block-guide-enum-coverage.test.ts | 1 + 39 files changed, 636 insertions(+), 115 deletions(-) create mode 100644 .changeset/social-icon-tone.md create mode 100644 packages/editor/tests/socialIconTone.test.ts diff --git a/.changeset/social-icon-tone.md b/.changeset/social-icon-tone.md new file mode 100644 index 000000000..1e15df530 --- /dev/null +++ b/.changeset/social-icon-tone.md @@ -0,0 +1,17 @@ +--- +"@templatical/types": minor +"@templatical/renderer": minor +"@templatical/editor": minor +"@templatical/import-mjml": patch +"@templatical/import-topol": patch +--- + +Social icons take a tone, separate from their shape + +`SocialIconsBlock` gains an optional `iconTone`: `"brand"` (each platform's own color, the default and what an absent value means), `"dark"` or `"light"`. `iconStyle` stays the shape and gains `"plain"`, the glyph alone with no badge or outline. A filled shape draws the glyph white on the tone, or near-black on `"light"`; `outlined` and `plain` draw in the tone. The editor offers both as separate selects, Style and Color. + +The renderer ships a PNG set for every style and tone: 6 × 3 sets of 17 platforms. The packed package grows from 411 KB to 1.31 MB (545 KB to 1.50 MB unpacked). A brand-colored icon keeps its URL, `{style}/{platform}.png`, byte for byte; a tone lives at `{style}-{tone}/{platform}.png`. If you self-host the icons through `socialIconsBaseUrl`, copy the new folders before using a tone or `plain`. + +`@templatical/types` exports `SocialIconTone`, `SOCIAL_ICON_STYLES`, `SOCIAL_ICON_TONES`, `SOCIAL_ICON_TONE_COLORS`, `socialIconColors()`, `socialIconAssetDir()` and `socialIconGlyphScale()`, the rules the editor, the renderer and the PNG build share. + +The MJML importer reads the style and tone back from a tone's folder, and the Topol importer maps its black-and-white `outlinedbw` set to `outlined` in `"dark"`. diff --git a/apps/docs/api/editor.md b/apps/docs/api/editor.md index 3ed26e376..7c0e883e3 100644 --- a/apps/docs/api/editor.md +++ b/apps/docs/api/editor.md @@ -71,7 +71,7 @@ unmount(); | `blockDefaults` | `BlockDefaults` | No | Default property overrides for new blocks. See [Defaults](/guide/defaults) | | `templateDefaults` | `TemplateDefaults` | No | Default template settings for empty templates. See [Defaults](/guide/defaults) | | `fonts` | `FontsConfig` | No | Font configuration. See [Custom Fonts](/guide/fonts) | -| `socialIconsBaseUrl` | `string` | No | Base URL for the social-icon PNGs in exported MJML, forwarded to the renderer’s [`socialIconsBaseUrl`](/api/renderer-typescript#social-icons). Set it to self-host the icons; omit to use the version-pinned jsDelivr default. Applies to the local `toMjml()` / `toHtml()` (via `compileMjml`) path only — a `render.toMjml` provider passes the URL to its own `renderToMjml` | +| `socialIconsBaseUrl` | `string` | No | Base URL for the social-icon PNGs in exported MJML, forwarded to the renderer’s [`socialIconsBaseUrl`](/api/renderer-typescript#social-icons). Set it to self-host the icons, mirroring every folder, including the `{style}-{tone}` folders for an `iconTone` of `dark` or `light`; omit to use the version-pinned jsDelivr default. Applies to the local `toMjml()` / `toHtml()` (via `compileMjml`) path only — a `render.toMjml` provider passes the URL to its own `renderToMjml` | | `colors` | `ColorsConfig` | No | Color-picker palette. `presets` render as a clickable grid in every picker; `allowCustom: false` locks authors to them. See [Preset colors](#preset-colors) | | `templateSettings` | `TemplateSettingsConfig` | No | Which template settings the Settings panel exposes. `fields` is an allowlist over the members of `TemplateSettings`, or `false` for none (which removes the tab). Omit for every setting. See [Restricting the settings panel](#restricting-the-settings-panel) | | `theme` | `ThemeOverrides` | No | Color token overrides. Supports a `dark` key for dark mode overrides. See [Theming](/guide/theming) | diff --git a/apps/docs/api/renderer-typescript.md b/apps/docs/api/renderer-typescript.md index a30a04e63..4118e7b23 100644 --- a/apps/docs/api/renderer-typescript.md +++ b/apps/docs/api/renderer-typescript.md @@ -70,7 +70,7 @@ interface RenderOptions { | `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. | | `blockRenderers` | `{}` | Per-type renderer overrides keyed by `block.type`. A countdown GIF backend uses this. An override owns the hidden-on-all-viewports check. | | `getCustomBlockStylesheet` | -- | `(customType) => string \| undefined \| null`. Called once per unique `customType`. Editor consumers pass the registry stylesheet. | -| `socialIconsBaseUrl` | version-pinned jsDelivr 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. | +| `socialIconsBaseUrl` | version-pinned jsDelivr URL | Base URL (no trailing slash) for the social icon PNG assets. Resolved per icon to `${baseUrl}/${dir}/${platform}.png`, where `dir` is the style, plus `-${tone}` for a `dark` or `light` `iconTone`. See [Social icons](#social-icons) below. | ### Custom blocks @@ -101,10 +101,10 @@ const mjml = await renderToMjml(content, { ### Social icons -Social icon blocks are emitted as ``. The default `socialIconsBaseUrl` points at the version-pinned jsDelivr mirror of `@templatical/renderer`, which ships pre-rasterized PNGs (every `SocialPlatform` × 5 styles) alongside the package: +Social icon blocks are emitted as ``. `dir` is the icon style for brand colors (`circle`) and `{style}-{tone}` for an `iconTone` of `dark` or `light` (`circle-dark`). The default `socialIconsBaseUrl` points at the version-pinned jsDelivr mirror of `@templatical/renderer`, which ships pre-rasterized PNGs (every `SocialPlatform` × 6 styles × 3 tones) alongside the package: ``` -https://cdn.jsdelivr.net/npm/@templatical/renderer@/assets/social/{style}/{platform}.png +https://cdn.jsdelivr.net/npm/@templatical/renderer@/assets/social/{dir}/{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. @@ -119,7 +119,7 @@ const mjml = await renderToMjml(content, { }); ``` -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 | website`. The shipped 192×192 PNGs are a reasonable starting point if you want to mirror them. +The exact filenames the renderer expects are `{dir}/{platform}.png`, where `dir` is a `style` of `solid | outlined | rounded | square | circle | plain`, followed by `-dark` or `-light` for those colors (a brand-colored icon has no suffix), and `platform` is one of `facebook | twitter | instagram | linkedin | youtube | tiktok | pinterest | email | whatsapp | telegram | discord | snapchat | reddit | github | dribbble | behance | website`. 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: diff --git a/apps/docs/api/types.md b/apps/docs/api/types.md index f12aaba7e..677760c00 100644 --- a/apps/docs/api/types.md +++ b/apps/docs/api/types.md @@ -304,6 +304,7 @@ interface SocialIconsBlock extends BaseBlock { type: 'social'; icons: SocialIcon[]; iconStyle: SocialIconStyle; + iconTone?: SocialIconTone; // absent means 'brand' iconSize: SocialIconSize; spacing: number; align: 'left' | 'center' | 'right'; @@ -323,7 +324,9 @@ type SocialPlatform = | 'website'; -type SocialIconStyle = 'solid' | 'outlined' | 'rounded' | 'square' | 'circle'; +type SocialIconStyle = + | 'solid' | 'outlined' | 'rounded' | 'square' | 'circle' | 'plain'; +type SocialIconTone = 'brand' | 'dark' | 'light'; type SocialIconSize = 'small' | 'medium' | 'large'; ``` diff --git a/apps/docs/de/api/editor.md b/apps/docs/de/api/editor.md index 22d240a49..62b41ca95 100644 --- a/apps/docs/de/api/editor.md +++ b/apps/docs/de/api/editor.md @@ -71,7 +71,7 @@ unmount(); | `blockDefaults` | `BlockDefaults` | Nein | Standard-Property-Überschreibungen für neue Blöcke. Siehe [Standardwerte](/de/guide/defaults) | | `templateDefaults` | `TemplateDefaults` | Nein | Standardeinstellungen für leere Templates. Siehe [Standardwerte](/de/guide/defaults) | | `fonts` | `FontsConfig` | Nein | Schriftart-Konfiguration. Siehe [Benutzerdefinierte Schriftarten](/de/guide/fonts) | -| `socialIconsBaseUrl` | `string` | Nein | Basis-URL für die Social-Icon-PNGs im exportierten MJML, weitergereicht an [`socialIconsBaseUrl`](/de/api/renderer-typescript#social-icons) des Renderers. Setzen Sie sie, um die Icons selbst zu hosten; ohne Angabe gilt der versionsgebundene jsDelivr-Standard. Betrifft nur den lokalen `toMjml()` / `toHtml()`-Pfad (über `compileMjml`) — eine `render.toMjml`-Implementierung übergibt die URL an ihr eigenes `renderToMjml` | +| `socialIconsBaseUrl` | `string` | Nein | Basis-URL für die Social-Icon-PNGs im exportierten MJML, weitergereicht an [`socialIconsBaseUrl`](/de/api/renderer-typescript#social-icons) des Renderers. Setzen Sie sie, um die Icons selbst zu hosten, und spiegeln Sie dabei alle Ordner, auch die `{style}-{tone}`-Ordner für einen `iconTone` von `dark` oder `light`; ohne Angabe gilt der versionsgebundene jsDelivr-Standard. Betrifft nur den lokalen `toMjml()` / `toHtml()`-Pfad (über `compileMjml`) — eine `render.toMjml`-Implementierung übergibt die URL an ihr eigenes `renderToMjml` | | `colors` | `ColorsConfig` | Nein | Farbwähler-Palette. `presets` werden als anklickbares Raster in jedem Farbwähler gerendert; `allowCustom: false` beschränkt Autoren darauf. Siehe [Vordefinierte Farben](#vordefinierte-farben) | | `templateSettings` | `TemplateSettingsConfig` | Nein | Welche Template-Einstellungen das Einstellungs-Panel anbietet. `fields` ist eine Positivliste über die Mitglieder von `TemplateSettings` oder `false` für keine (dann entfällt der Tab). Ohne Angabe sind alle Einstellungen bearbeitbar. Siehe [Einstellungs-Panel einschränken](#einstellungs-panel-einschraenken) | | `theme` | `ThemeOverrides` | Nein | Überschreibungen für Farb-Tokens. Unterstützt einen `dark`-Schlüssel für Dark-Mode-Überschreibungen. Siehe [Theming](/de/guide/theming) | diff --git a/apps/docs/de/api/renderer-typescript.md b/apps/docs/de/api/renderer-typescript.md index a1805ee10..454871e50 100644 --- a/apps/docs/de/api/renderer-typescript.md +++ b/apps/docs/de/api/renderer-typescript.md @@ -70,7 +70,7 @@ interface RenderOptions { | `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. | | `blockRenderers` | `{}` | Typbezogene Renderer-Overrides, keyed nach `block.type`. Ein Countdown-GIF-Backend nutzt dies. Ein Override übernimmt die Prüfung auf „auf allen Viewports ausgeblendet“. | | `getCustomBlockStylesheet` | -- | `(customType) => string \| undefined \| null`. Wird einmal pro eindeutigem `customType` aufgerufen. Editor-Konsumenten übergeben das Registry-Stylesheet. | -| `socialIconsBaseUrl` | versionsgebundene jsDelivr-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. | +| `socialIconsBaseUrl` | versionsgebundene jsDelivr-URL | Basis-URL (ohne abschließenden Schrägstrich) für die PNG-Assets der Social-Media-Icons. Wird pro Icon zu `${baseUrl}/${dir}/${platform}.png` aufgelöst, wobei `dir` der Stil ist, bei einem `iconTone` von `dark` oder `light` gefolgt von `-${tone}`. Siehe [Social-Media-Icons](#social-media-icons) unten. | ### Benutzerdefinierte Blöcke @@ -101,10 +101,10 @@ const mjml = await renderToMjml(content, { ### Social-Media-Icons -Social-Icon-Blöcke werden als `` ausgegeben. Der Standardwert von `socialIconsBaseUrl` verweist auf den versionsgebundenen jsDelivr-Mirror von `@templatical/renderer`, der vorgerasterte PNGs (jede `SocialPlatform` × 5 Stile) mit dem Paket ausliefert: +Social-Icon-Blöcke werden als `` ausgegeben. `dir` ist bei Markenfarben der Icon-Stil (`circle`) und bei einem `iconTone` von `dark` oder `light` `{style}-{tone}` (`circle-dark`). Der Standardwert von `socialIconsBaseUrl` verweist auf den versionsgebundenen jsDelivr-Mirror von `@templatical/renderer`, der vorgerasterte PNGs (jede `SocialPlatform` × 6 Stile × 3 Farbtöne) mit dem Paket ausliefert: ``` -https://cdn.jsdelivr.net/npm/@templatical/renderer@/assets/social/{style}/{platform}.png +https://cdn.jsdelivr.net/npm/@templatical/renderer@/assets/social/{dir}/{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. @@ -119,7 +119,7 @@ const mjml = await renderToMjml(content, { }); ``` -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 | website` ist. Die ausgelieferten 192×192-PNGs sind ein sinnvoller Ausgangspunkt, wenn Sie sie spiegeln möchten. +Die exakten Dateinamen, die der Renderer erwartet, sind `{dir}/{platform}.png`, wobei `dir` ein `style` aus `solid | outlined | rounded | square | circle | plain` ist, für diese Farben gefolgt von `-dark` oder `-light` (ein Icon in Markenfarbe hat kein Suffix), und `platform` einer von `facebook | twitter | instagram | linkedin | youtube | tiktok | pinterest | email | whatsapp | telegram | discord | snapchat | reddit | github | dribbble | behance | website` 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: diff --git a/apps/docs/de/api/types.md b/apps/docs/de/api/types.md index cc3a70356..e7aa7ac9b 100644 --- a/apps/docs/de/api/types.md +++ b/apps/docs/de/api/types.md @@ -304,6 +304,7 @@ interface SocialIconsBlock extends BaseBlock { type: 'social'; icons: SocialIcon[]; iconStyle: SocialIconStyle; + iconTone?: SocialIconTone; // ohne Angabe: 'brand' iconSize: SocialIconSize; spacing: number; align: 'left' | 'center' | 'right'; @@ -323,7 +324,9 @@ type SocialPlatform = | 'website'; -type SocialIconStyle = 'solid' | 'outlined' | 'rounded' | 'square' | 'circle'; +type SocialIconStyle = + | 'solid' | 'outlined' | 'rounded' | 'square' | 'circle' | 'plain'; +type SocialIconTone = 'brand' | 'dark' | 'light'; type SocialIconSize = 'small' | 'medium' | 'large'; ``` diff --git a/apps/docs/de/guide/blocks.md b/apps/docs/de/guide/blocks.md index de6b9f61c..2d1bce360 100644 --- a/apps/docs/de/guide/blocks.md +++ b/apps/docs/de/guide/blocks.md @@ -123,11 +123,14 @@ Eine Reihe von Social-Media-Icons, die zu Plattformprofilen verlinken. | Eigenschaft | Typ | Beschreibung | |----------|------|-------------| | `icons` | `SocialIcon[]` | Liste der Social Icons | -| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle'` | Visueller Stil | +| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle' \| 'plain'` | Form. `plain` ist das Symbol allein, ohne Fläche oder Umriss | +| `iconTone` | `'brand' \| 'dark' \| 'light'` | Optional. Die Markenfarbe jeder Plattform (Standard) oder ein Farbton für alle Icons. Eine gefüllte Form zeigt das Symbol weiß auf dem Farbton, bei `light` fast schwarz | | `iconSize` | `'small' \| 'medium' \| 'large'` | Icon-Größe | | `spacing` | `number` | Abstand zwischen Icons in px | | `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | +E-Mail-Clients, die in den Dunkelmodus wechseln, etwa die Gmail-App und Outlook, dunkeln den Hintergrund ab, lassen Bilder aber unverändert, sodass ein `plain`- oder `outlined`-Icon in `dark` verschwinden kann. Wählen Sie für dunkle Icons besser eine gefüllte Form, deren weißes Symbol sichtbar bleibt. + 17 Plattformen werden unterstützt: Facebook, Twitter/X, Instagram, LinkedIn, YouTube, TikTok, Pinterest, E-Mail, Website, WhatsApp, Telegram, Discord, Snapchat, Reddit, GitHub, Dribbble und Behance. Jedes `SocialIcon` hat: diff --git a/apps/docs/guide/blocks.md b/apps/docs/guide/blocks.md index 30f3198d7..3fee6c347 100644 --- a/apps/docs/guide/blocks.md +++ b/apps/docs/guide/blocks.md @@ -123,11 +123,14 @@ A row of social media icons linking to platform profiles. | Property | Type | Description | |----------|------|-------------| | `icons` | `SocialIcon[]` | List of social icons | -| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle'` | Visual style | +| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle' \| 'plain'` | Shape. `plain` is the glyph alone, with no badge or outline | +| `iconTone` | `'brand' \| 'dark' \| 'light'` | Optional. Each platform's brand color (the default), or one tone for every icon. A filled shape draws the glyph in white on the tone, or near-black on `light` | | `iconSize` | `'small' \| 'medium' \| 'large'` | Icon size | | `spacing` | `number` | Space between icons in px | | `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +Email clients that switch to dark mode, such as the Gmail app and Outlook, darken the background but leave images as they are, so a `plain` or `outlined` icon in `dark` can disappear. For dark icons, prefer a filled shape: its white glyph stays visible. + 17 platforms are supported: Facebook, Twitter/X, Instagram, LinkedIn, YouTube, TikTok, Pinterest, Email, Website, WhatsApp, Telegram, Discord, Snapchat, Reddit, GitHub, Dribbble, and Behance. Each `SocialIcon` has: diff --git a/apps/docs/public/llms-full.txt b/apps/docs/public/llms-full.txt index 470d701a1..2644ca2c7 100644 --- a/apps/docs/public/llms-full.txt +++ b/apps/docs/public/llms-full.txt @@ -74,7 +74,7 @@ unmount(); | `blockDefaults` | `BlockDefaults` | No | Default property overrides for new blocks. See [Defaults](/guide/defaults) | | `templateDefaults` | `TemplateDefaults` | No | Default template settings for empty templates. See [Defaults](/guide/defaults) | | `fonts` | `FontsConfig` | No | Font configuration. See [Custom Fonts](/guide/fonts) | -| `socialIconsBaseUrl` | `string` | No | Base URL for the social-icon PNGs in exported MJML, forwarded to the renderer’s [`socialIconsBaseUrl`](/api/renderer-typescript#social-icons). Set it to self-host the icons; omit to use the version-pinned jsDelivr default. Applies to the local `toMjml()` / `toHtml()` (via `compileMjml`) path only — a `render.toMjml` provider passes the URL to its own `renderToMjml` | +| `socialIconsBaseUrl` | `string` | No | Base URL for the social-icon PNGs in exported MJML, forwarded to the renderer’s [`socialIconsBaseUrl`](/api/renderer-typescript#social-icons). Set it to self-host the icons, mirroring every folder, including the `{style}-{tone}` folders for an `iconTone` of `dark` or `light`; omit to use the version-pinned jsDelivr default. Applies to the local `toMjml()` / `toHtml()` (via `compileMjml`) path only — a `render.toMjml` provider passes the URL to its own `renderToMjml` | | `colors` | `ColorsConfig` | No | Color-picker palette. `presets` render as a clickable grid in every picker; `allowCustom: false` locks authors to them. See [Preset colors](#preset-colors) | | `templateSettings` | `TemplateSettingsConfig` | No | Which template settings the Settings panel exposes. `fields` is an allowlist over the members of `TemplateSettings`, or `false` for none (which removes the tab). Omit for every setting. See [Restricting the settings panel](#restricting-the-settings-panel) | | `theme` | `ThemeOverrides` | No | Color token overrides. Supports a `dark` key for dark mode overrides. See [Theming](/guide/theming) | @@ -748,7 +748,7 @@ interface RenderOptions { | `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. | | `blockRenderers` | `{}` | Per-type renderer overrides keyed by `block.type`. A countdown GIF backend uses this. An override owns the hidden-on-all-viewports check. | | `getCustomBlockStylesheet` | -- | `(customType) => string \| undefined \| null`. Called once per unique `customType`. Editor consumers pass the registry stylesheet. | -| `socialIconsBaseUrl` | version-pinned jsDelivr 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. | +| `socialIconsBaseUrl` | version-pinned jsDelivr URL | Base URL (no trailing slash) for the social icon PNG assets. Resolved per icon to `${baseUrl}/${dir}/${platform}.png`, where `dir` is the style, plus `-${tone}` for a `dark` or `light` `iconTone`. See [Social icons](#social-icons) below. | ### Custom blocks @@ -779,10 +779,10 @@ const mjml = await renderToMjml(content, { ### Social icons -Social icon blocks are emitted as ``. The default `socialIconsBaseUrl` points at the version-pinned jsDelivr mirror of `@templatical/renderer`, which ships pre-rasterized PNGs (every `SocialPlatform` × 5 styles) alongside the package: +Social icon blocks are emitted as ``. `dir` is the icon style for brand colors (`circle`) and `{style}-{tone}` for an `iconTone` of `dark` or `light` (`circle-dark`). The default `socialIconsBaseUrl` points at the version-pinned jsDelivr mirror of `@templatical/renderer`, which ships pre-rasterized PNGs (every `SocialPlatform` × 6 styles × 3 tones) alongside the package: ``` -https://cdn.jsdelivr.net/npm/@templatical/renderer@/assets/social/{style}/{platform}.png +https://cdn.jsdelivr.net/npm/@templatical/renderer@/assets/social/{dir}/{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. @@ -797,7 +797,7 @@ const mjml = await renderToMjml(content, { }); ``` -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 | website`. The shipped 192×192 PNGs are a reasonable starting point if you want to mirror them. +The exact filenames the renderer expects are `{dir}/{platform}.png`, where `dir` is a `style` of `solid | outlined | rounded | square | circle | plain`, followed by `-dark` or `-light` for those colors (a brand-colored icon has no suffix), and `platform` is one of `facebook | twitter | instagram | linkedin | youtube | tiktok | pinterest | email | whatsapp | telegram | discord | snapchat | reddit | github | dribbble | behance | website`. 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: @@ -1427,6 +1427,7 @@ interface SocialIconsBlock extends BaseBlock { type: 'social'; icons: SocialIcon[]; iconStyle: SocialIconStyle; + iconTone?: SocialIconTone; // absent means 'brand' iconSize: SocialIconSize; spacing: number; align: 'left' | 'center' | 'right'; @@ -1446,7 +1447,9 @@ type SocialPlatform = | 'website'; -type SocialIconStyle = 'solid' | 'outlined' | 'rounded' | 'square' | 'circle'; +type SocialIconStyle = + | 'solid' | 'outlined' | 'rounded' | 'square' | 'circle' | 'plain'; +type SocialIconTone = 'brand' | 'dark' | 'light'; type SocialIconSize = 'small' | 'medium' | 'large'; ``` @@ -6752,11 +6755,14 @@ A row of social media icons linking to platform profiles. | Property | Type | Description | |----------|------|-------------| | `icons` | `SocialIcon[]` | List of social icons | -| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle'` | Visual style | +| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle' \| 'plain'` | Shape. `plain` is the glyph alone, with no badge or outline | +| `iconTone` | `'brand' \| 'dark' \| 'light'` | Optional. Each platform's brand color (the default), or one tone for every icon. A filled shape draws the glyph in white on the tone, or near-black on `light` | | `iconSize` | `'small' \| 'medium' \| 'large'` | Icon size | | `spacing` | `number` | Space between icons in px | | `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +Email clients that switch to dark mode, such as the Gmail app and Outlook, darken the background but leave images as they are, so a `plain` or `outlined` icon in `dark` can disappear. For dark icons, prefer a filled shape: its white glyph stays visible. + 17 platforms are supported: Facebook, Twitter/X, Instagram, LinkedIn, YouTube, TikTok, Pinterest, Email, Website, WhatsApp, Telegram, Discord, Snapchat, Reddit, GitHub, Dribbble, and Behance. Each `SocialIcon` has: diff --git a/packages/editor/src/components/blocks/SocialIconSvg.vue b/packages/editor/src/components/blocks/SocialIconSvg.vue index e93fd3e06..e64252dbb 100644 --- a/packages/editor/src/components/blocks/SocialIconSvg.vue +++ b/packages/editor/src/components/blocks/SocialIconSvg.vue @@ -1,20 +1,25 @@