diff --git a/.changeset/social-icon-tone.md b/.changeset/social-icon-tone.md new file mode 100644 index 000000000..fe2c90c5d --- /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, about 1.9 MB in the package. 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()` and `socialIconAssetDir()`, 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/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/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..829dc465b 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; // fehlt: '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..dccc5a773 100644 --- a/apps/docs/de/guide/blocks.md +++ b/apps/docs/de/guide/blocks.md @@ -123,7 +123,8 @@ 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 | diff --git a/apps/docs/guide/blocks.md b/apps/docs/guide/blocks.md index 30f3198d7..7b3552011 100644 --- a/apps/docs/guide/blocks.md +++ b/apps/docs/guide/blocks.md @@ -123,7 +123,8 @@ 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 | diff --git a/apps/docs/public/llms-full.txt b/apps/docs/public/llms-full.txt index e439897a0..1d25cdf87 100644 --- a/apps/docs/public/llms-full.txt +++ b/apps/docs/public/llms-full.txt @@ -746,7 +746,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 @@ -777,10 +777,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. @@ -795,7 +795,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: @@ -1425,6 +1425,7 @@ interface SocialIconsBlock extends BaseBlock { type: 'social'; icons: SocialIcon[]; iconStyle: SocialIconStyle; + iconTone?: SocialIconTone; // absent means 'brand' iconSize: SocialIconSize; spacing: number; align: 'left' | 'center' | 'right'; @@ -1444,7 +1445,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'; ``` @@ -4992,7 +4995,8 @@ 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 | 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 @@