Skip to content

feat: Shadow DOM isolation for WP plugin with deferred CSS loading - #664

Open
jackgranatowski wants to merge 2 commits into
mainfrom
claude/konfigurator-opcje-tokeny-rmorrr
Open

feat: Shadow DOM isolation for WP plugin with deferred CSS loading#664
jackgranatowski wants to merge 2 commits into
mainfrom
claude/konfigurator-opcje-tokeny-rmorrr

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds Shadow DOM isolation for the configurator when embedded in WordPress admin, protecting it from interference by other admin plugins' CSS and JavaScript. The panel now mounts inside a shadow root with its stylesheet linked inside the shadow boundary (preventing style leakage), and mounting is deferred until the stylesheet loads to avoid unstyled flashes.

Standalone mode (light DOM mount into #app) is unchanged. WordPress plugin mode now:

  1. Detects a same-origin cssUrl provided via window.slashedApp.cssUrl
  2. Creates a shadow root on #slashed-admin-app if Shadow DOM is supported
  3. Links the panel CSS inside the shadow (not in the document head, since head styles don't cross the boundary)
  4. Defers mounting until the stylesheet loads (or 5s timeout, whichever comes first)
  5. Falls back to light DOM mount if Shadow DOM is unavailable or cssUrl is missing/cross-origin

Also adds a note to the "Link visited" color override in ColorsPanel explaining the browser privacy restriction that prevents live preview of :visited styling.

Type

  • feat
  • fix
  • docs
  • chore / tooling

Checklist

  • Conventional Commit messages (feat:, fix:, docs:, …) — enforced by commitlint
  • npm run lint:css passes (stylelint)
  • npm run build rebuilds dist/ (bundles are git-ignored; CI rebuilds and stamps headers)
  • npm test passes (unit + Playwright e2e)
  • Version references in sync if any version-related file changed (npm run check:version)
  • LLM guide reviewed/updated if core/*.css, optional/*.css, or token-registry.json changed (npm run check:llm-guide)
  • Generated artifacts regenerated, not hand-edited (npm run check:macros, check:registry, audit:check)
  • CHANGELOG.md updated under ## [Unreleased] (for user-facing changes)
  • Breaking changes include migration docs

Notes

Manual testing:

  • Standalone mode: verify the app mounts normally into #app with light DOM
  • WordPress plugin mode with Shadow DOM support and valid cssUrl: verify the app mounts into a shadow root, styles load inside the shadow, and no unstyled flash occurs
  • WordPress plugin mode without Shadow DOM or without cssUrl: verify fallback to light DOM mount works
  • Verify the "Link visited" note appears in the Colors panel and explains the browser privacy restriction

Implementation details:

  • embeddedCssUrl() safely reads window.slashedApp.cssUrl with type guards
  • isSameOrigin() validates the CSS URL before loading it into the shadow
  • The 5-second timeout ensures the panel appears even if the stylesheet request stalls
  • The mounted flag prevents double-mounting if both load and error events fire

https://claude.ai/code/session_01PtvNAKsrEE1mynifQbjSun

Summary by CodeRabbit

  • New Features

    • Added explanatory notes beneath applicable color settings, including guidance about visited-link privacy behavior.
    • Embedded installations now support improved style isolation through Shadow DOM when available.
    • Added automatic loading of configured stylesheets before displaying embedded content.
  • Bug Fixes

    • Embedded installations now validate stylesheet locations and gracefully fall back when isolation or stylesheet loading is unavailable.
    • Added a timeout and error handling to prevent embedded app loading from becoming stuck.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jackgranatowski, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ce9badb-edb7-42dc-b7c1-3128c865ac7a

📥 Commits

Reviewing files that changed from the base of the PR and between 5d61cc8 and 74e86b7.

📒 Files selected for processing (1)
  • configurator/src/main.ts
📝 Walkthrough

Walkthrough

The PR adds optional explanatory notes to semantic color overrides. It also updates embedded WordPress mounting to use an open Shadow DOM with stylesheet loading, timeout handling, and light-DOM fallback.

Changes

Configurator mounting

Layer / File(s) Summary
Embedded mounting and stylesheet loading
configurator/src/main.ts
The configurator validates window.slashedApp.cssUrl, prefers an open Shadow DOM for embedded mounting, waits for stylesheet load or failure, and falls back to light-DOM mounting when needed. Standalone mounting remains direct.

Semantic override notes

Layer / File(s) Summary
Semantic override notes and display
configurator/src/components/panels/ColorsPanel.svelte
Semantic override entries now support optional notes. The visited-link override explains browser privacy restrictions, and notes render beneath matching color inputs.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant main.ts
  participant window.slashedApp
  participant ShadowRoot
  participant Stylesheet
  participant App
  main.ts->>window.slashedApp: Read and validate cssUrl
  main.ts->>ShadowRoot: Create open shadow root
  ShadowRoot->>Stylesheet: Load internal stylesheet
  Stylesheet-->>main.ts: Load, error, or timeout
  main.ts->>App: Apply theme root and mount app
Loading

Possibly related PRs

Suggested labels: codex

Suggested reviewers: claude

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: Shadow DOM isolation and deferred CSS loading for the WordPress plugin.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/konfigurator-opcje-tokeny-rmorrr

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the codex label Aug 8, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@configurator/src/main.ts`:
- Around line 64-78: Update the Shadow DOM mounting logic around wpHost and
attachShadow so it no longer claims or implies JavaScript isolation; describe
the protection as CSS and DOM encapsulation only. Keep the existing Shadow DOM
behavior and light-DOM fallback unchanged unless the implementation explicitly
requires isolation from host scripts, in which case replace the mount with an
isolated iframe document.
- Around line 31-34: Update embeddedCssUrl() to reject stylesheet URLs that
contain only whitespace, while continuing to return valid non-empty URLs
unchanged. Apply the validation before the URL is consumed by the Shadow DOM
mounting flow.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a8f61809-e09b-4ac0-a92b-5a722e5869a6

📥 Commits

Reviewing files that changed from the base of the PR and between 54a85db and 5d61cc8.

📒 Files selected for processing (2)
  • configurator/src/components/panels/ColorsPanel.svelte
  • configurator/src/main.ts

Comment thread configurator/src/main.ts Outdated
Comment thread configurator/src/main.ts Outdated
claude added 2 commits August 8, 2026 22:48
Browsers restrict :visited styling for privacy, so the preview's links
(never visited) can't repaint with a --sf-color-link--visited override — it
only takes effect on genuinely-visited links on the published site. The
control was correct but read as broken; add a short inline note on the row
so the behaviour is expected rather than surprising.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PtvNAKsrEE1mynifQbjSun
When mounted into a host page's #slashed-admin-app (the WordPress plugin),
the panel shared one document with every other admin plugin's CSS and JS. A
competing reset or a broad rule like `* { pointer-events: none }` could
leave it fully rendered but non-interactive, while the frontend overlay
(already in a Shadow DOM) stayed fine on the same site.

Mount the embedded panel inside a Shadow DOM too. This is CSS/DOM
encapsulation, not a JS sandbox (an open shadow root shares the host realm),
but it is what the failure needs: host styles no longer cross in, and
Svelte's delegated listeners bind to the shadow-internal root so host
document-level handlers can't preempt them. The panel stylesheet is
preloaded, then linked inside the shadow once it loads (head styles don't
cross the boundary); on stylesheet error or a stalled load we fall back to
the previous light-DOM mount, which the plugin's head-enqueued app.css
still styles. attachShadow is deferred until load success so a failed
stylesheet can never trap the panel unstyled. Standalone (#app) is
unchanged. embeddedCssUrl() trims its input so a whitespace-only value
can't pass the same-origin check and mount without a stylesheet.

Verified headlessly: under a hostile `* { pointer-events: none !important }`
rule plus a clobbered window.wp, the shadow-isolated panel renders fully and
responds to clicks; a 404 stylesheet falls back to a styled light-DOM mount.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PtvNAKsrEE1mynifQbjSun

Copy link
Copy Markdown
Contributor Author

CI status — green except a pre-existing check

All functional and lint checks pass on 74e86b7: Configurator tests ✅, Regression tests ✅, Build bundle ✅, Verify all generated artifacts ✅, Lint CSS ✅, Lint commit messages ✅, CodeQL ✅.

The one red check is Dependency vulnerability audit, which is pre-existing on main — this PR touches only configurator/src/main.ts and configurator/src/components/panels/ColorsPanel.svelte, with no changes to package.json/lockfiles, so it neither introduces nor can resolve that alert.

Both CodeRabbit review threads (whitespace-only cssUrl, and the "Shadow DOM ≠ JS isolation" wording) are addressed and resolved.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants