Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/shadow-dom-default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@templatical/editor": minor
---

Mount the editor inside a Shadow DOM by default. `init({ container })` now resolve `shadowDom: true` when the option is omitted — host page stylesheets no longer cascade into editor elements (`p`, `h1`, `a`, `input`, etc.) via tag selectors, closing [issue #70](https://github.com/templatical/sdk/issues/70).

**Behavior changes consumers may notice:**

- External `document.querySelector("#editor .tpl-…")` queries no longer reach editor internals because the editor's DOM lives inside `container.shadowRoot`. Walk the shadow root explicitly (`container.shadowRoot.querySelector(...)`) or opt out with `shadowDom: false`.
- Host stylesheets that intentionally styled editor elements via element selectors stop applying. The supported theming protocol is now the `--tpl-user-*` CSS custom property namespace — set `--tpl-user-primary`, `--tpl-user-radius-md`, etc. on the editor container (or any ancestor) and the override inherits across the shadow boundary. The existing `theme` config option still takes precedence and works unchanged.
- Browser minimums in default mode bump to Firefox 101+ and Safari 16.4+ (required by the `adoptedStyleSheets` API). Chrome / Edge 80+ is unchanged. Pass `shadowDom: false` to keep the previous light-DOM mount with broader browser support.

The `shadowDom: false` escape hatch remains supported.
29 changes: 11 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ jobs:

e2e:
runs-on: ubuntu-latest
# Playwright's official image ships Chromium + every system dep
# preinstalled, skipping the ~5 min `playwright install-deps` step that
# fresh ubuntu-latest runners would otherwise pay every run (apt state
# isn't cacheable across runs). Image tag must match the pinned
# `@playwright/test` version in pnpm-lock.yaml.
container:
image: mcr.microsoft.com/playwright:v1.59.1-jammy
needs: [build]
strategy:
fail-fast: false
Expand All @@ -87,15 +94,6 @@ jobs:
with:
name: dist
path: packages/
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm exec playwright install --with-deps chromium
if: steps.playwright-cache.outputs.cache-hit != 'true'
- run: pnpm exec playwright install-deps chromium
if: steps.playwright-cache.outputs.cache-hit == 'true'
- run: pnpm exec playwright test --shard=${{ matrix.shard }}/3
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: ${{ !cancelled() }}
Expand All @@ -115,19 +113,14 @@ jobs:
# (after publish) and every feature PR's run still cover this surface.
if: ${{ !startsWith(github.head_ref, 'changeset-release/') && !startsWith(github.ref_name, 'changeset-release/') }}
runs-on: ubuntu-latest
# Same rationale as the `e2e` job — use the Playwright image to skip
# the system-dep install step.
container:
image: mcr.microsoft.com/playwright:v1.59.1-jammy
needs: [build]
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: ./.github/actions/setup
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm exec playwright install --with-deps chromium
if: steps.playwright-cache.outputs.cache-hit != 'true'
- run: pnpm exec playwright install-deps chromium
if: steps.playwright-cache.outputs.cache-hit == 'true'
- run: pnpm run test:e2e:consumer
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: failure()
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ Things that are usually paid features in commercial editors — open-source in T
### And more

- **Drop-in mount** — one `init()` call, one `unmount()`. No framework lock-in.
- **Style-isolated, both directions** — Shadow DOM by default keeps host CSS out of the editor; `tpl:` Tailwind prefix and no preflight reset keep editor styles out of your app. Drops into any page, any framework, any CMS — no resets, no conflicts. [Learn more →](https://docs.templatical.com/guide/shadow-dom)
- **14 block types** — Title, Paragraph, Image, Button, Section, Divider, Spacer, Social Icons, Menu, Table, HTML, Video, Countdown, Custom.
- **JSON templates** — portable, versionable, store anywhere, render anywhere.
- **MJML output** — works with any email provider (Postmark, Resend, SES, Mailgun, anything).
- **Framework-agnostic** — first-class examples for React, Vue, Svelte, Angular, vanilla.
- **Tailwind 4 with `tpl:` prefix** — no preflight reset, no style leaks into your app.
- **Bilingual** — en/de built in, easy to add more locales.
- **TypeScript strict** — full types for blocks, config, and callbacks.
- **Battle-tested** — ~1,400 unit tests + Playwright E2E coverage.
Expand Down
2 changes: 2 additions & 0 deletions apps/docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ const enSidebar: DefaultTheme.SidebarMulti = {
{ text: "Block & Template Defaults", link: "/guide/defaults" },
{ text: "Custom Fonts", link: "/guide/fonts" },
{ text: "Internationalization", link: "/guide/i18n" },
{ text: "Shadow DOM", link: "/guide/shadow-dom" },
],
},
{
Expand Down Expand Up @@ -244,6 +245,7 @@ const deSidebar: DefaultTheme.SidebarMulti = {
{ text: "Block- & Template-Standards", link: "/de/guide/defaults" },
{ text: "Benutzerdefinierte Schriften", link: "/de/guide/fonts" },
{ text: "Internationalisierung", link: "/de/guide/i18n" },
{ text: "Shadow DOM", link: "/de/guide/shadow-dom" },
],
},
{
Expand Down
Loading
Loading