Skip to content

feat(platform-browser): Add IsolatedShadowDom encapsulation #62723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

ryan-bendel
Copy link
Contributor

@ryan-bendel ryan-bendel commented Jul 21, 2025

Add IsolatedShadowDom encapsulation mode, which fixes style leakage by removing sharedstyleshost from dom-renderer IsolatedShadowDom class.

PR Checklist

Please check if your PR fulfills the following requirements:

Does this PR introduce a breaking change?

  • Yes
  • No

IsolatedShadowDom follows the spec, shadowdom does not now add <style> tags unrelated/not imported/defined by the component, which is caused by the sharedstyleshost.

Updated docs.

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.dev application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

Issue Number: 57801

Follow up to the PR discussion in #62357

The current Shadowdom implementation is incorrect, and does not adhere to the spec. Shadowdom components should be encapsulated with no style leakage, but currently, external styles are added that are completely unrelated to the shadow component. This causes significant bloat.

Before:

Screenshot 2025-06-27 at 15 32 02

After:

Screenshot 2025-06-28 at 07 12 54

What is the new behavior?

To ensure this is a smooth transition for users, the old behaviour has been preserved in a the ShadowDom encapsulation class. IsolatedShadowDom has been created to allow users to implement the fixed shadowdom behaviour, which does not use sharedstyleshost.

@pullapprove pullapprove bot requested review from atscott and dgp1130 July 21, 2025 07:56
@angular-robot angular-robot bot added detected: feature PR contains a feature commit area: core Issues related to the framework runtime labels Jul 21, 2025
@ngbot ngbot bot added this to the Backlog milestone Jul 21, 2025
@ryan-bendel
Copy link
Contributor Author

@thePunderWoman - Hey 👋

This is the new PR as we discussed in #62357 where i've taken the approach of migrating anyone on the existing shadowdom onto a new LegacyShadowDom and marked that as deprecated, so that we don't break anything for existing users.

I have manually tested running the migration locally, seems to work! One thing i am not 100% on though, as I predominantly use NX (which overrides ng commands) and runs it's own migrations on upgrades - is does Angular automatically run these migrations when you do an ng upgrade ? If so, that's good (kind of what we'd want i think)

Thanks!

@ryan-bendel ryan-bendel changed the title feat(platform-browser): Add legacyshadowdom encapsulation feat(platform-browser): Fix Shadowdom, and add legacyshadowdom encapsulation Jul 21, 2025
@JeanMeche
Copy link
Member

As is, this is considered a breaking change as the ShadowDom encapsulation changes behavior (even with the migration).

This probably needs to be discussed if the best is to keep ShadowDom as is (would reduce the burden of landing the change) and introduce a new symbol for the new behavior.

@ryan-bendel
Copy link
Contributor Author

ryan-bendel commented Jul 21, 2025

As is, this is considered a breaking change as the ShadowDom encapsulation changes behavior (even with the migration).

This probably needs to be discussed if the best is to keep ShadowDom as is (would reduce the burden of landing the change) and introduce a new symbol for the new behavior.

I'll mark as breaking 👍

Yeah the other option i mentioned was just creating a new encapsulation mode with the "fixed" shadowdom

But I leaned towards this as any new apps/components spun up would get the fixed behaviour, and it was suggested in the other PR it would be a lot of work for the team/users to change everything if you landed just the fixed version. By marking it as deprecated users would at least be nudged in the right direction, and hopefully make the Angular teams job a bit easier in the future 🤞

Otherwise we're just kicking the can down the road and you'll have two sets of encapsulations for shadowdom (arguably confusing), more to maintain, and in theory more components to fix later down the line when you pick it up from your backlog

@JeanMeche JeanMeche modified the milestones: Backlog, v21 Candidate Jul 21, 2025
@dgp1130
Copy link
Contributor

dgp1130 commented Jul 21, 2025

Regarding priority of this PR: I was recently exploring using shadow DOM to isolate styles across multiple applications within the same page (MicA). One challenge is that CdkOverlay renders outside the main application root, so any styles are lost there. I had explored creating a shadow root there as well and noticed the same problem that styles for the entire application are unnecessarily duplicated in the extra root. I think this PR might be useful for that effort, but I'm not yet sure if using shadow roots is the right approach for the broader style isolation problem I'm looking at in that context, hopefully I can get more insight on that soon.

Regarding this PR more specifically: I wonder if this setting should be configured via dependency injection rather than creating a new ViewEncapsulation mode? It feels easier to configure a single token rather than migrating each component individually.

A token does potentially make it a bit harder to incrementally roll out this change, but I suspect it's not that breaking and maybe doesn't need to have a super fine-grained opt-out? You could certainly use DI to opt-out specific components, though the hierarchy is kind of working against you there since opting-out one component naturally opts-out its dependencies, which isn't really what you want.

I'm mainly just asking whether this is worth a new ViewEncapsulation mode or whether we can do something simpler with DI? I'll defer to other framework folks on whether that's worth seriously considering, just an idea.

@thePunderWoman
Copy link
Contributor

@ryan-bendel Looks like this needs a few goldens updated. yarn run symbol-extractor:update should update the bundle symbols and yarn bazel run //packages/core:core_api.accept should update the core symbols. Can you do that and then re-push? That should get CI closer to green.

@ryan-bendel
Copy link
Contributor Author

@thePunderWoman ohhh I didn't know about that step, thanks! Pushed that now

@ryan-bendel
Copy link
Contributor Author

ryan-bendel commented Jul 22, 2025

I'll look at the failing tests, will run the same command locally the action is using.

Not 100% clear on what is needed in some targets, for example, pkg_npm only has one migration (and my new one) defined, but the migrations folder has multiple migrations in it. So likely i'm misunderstanding what is supposed to be defined where.

Side note, i've never once been able to run the test_firefox tests without it falling over 😆

@ryan-bendel
Copy link
Contributor Author

Grrr. I might need a bit of help with this if one of the Angular team wouldn't mind jumping on my branch?

ERROR: /home/runner/work/angular/angular/packages/core/schematics/migrations/shadowdom-migration/BUILD.bazel:10:11: //packages/core/schematics/migrations/shadowdom-migration:shadowdom-migration_rjs: no such attribute 'interop_deps' in 'ts_project' rule

I can't work out what is going on here, this build file is identical in all but name to the test-bed-get migration, but fails.

Really struggling to run the full test suite locally because it can't seem to locate the Firefox bin and bombs out. Just doing a build of the core package passes, so i've no clue what's going on 😢

@thePunderWoman thePunderWoman force-pushed the add-legacyshadowdom-encapsulation branch from e0f927a to 966bc88 Compare July 24, 2025 11:43
@thePunderWoman
Copy link
Contributor

@ryan-bendel Looks like there were some recent change with these bazel rules. interop_deps can be rolled into deps. I updated it and pushed, and I also collapsed the commits into the one. Hopefully it should not have issues with that during build anymore.

@ryan-bendel
Copy link
Contributor Author

@ryan-bendel Looks like there were some recent change with these bazel rules. interop_deps can be rolled into deps. I updated it and pushed, and I also collapsed the commits into the one. Hopefully it should not have issues with that during build anymore.

@thePunderWoman you're a star ⭐ - I think i tried that in a previous commit, and may have ended up failing elsewhere so i reverted, lets see how it goes this time 🤞

@ryan-bendel
Copy link
Contributor Author

Ah yes:

packages/core/schematics/migrations/shadowdom-migration/index.ts:10:36 - error TS2307: Cannot find module '../../utils/tsurge/helpers/angular_devkit' or its corresponding type declarations.

10 import {runMigrationInDevkit} from '../../utils/tsurge/helpers/angular_devkit';

Which confused me, as it's the same import path as other migrations. I'd lost the will to live at this point 😆

@thePunderWoman
Copy link
Contributor

thePunderWoman commented Jul 24, 2025

@ryan-bendel looks like it did get past that problem, but now has ✨other✨ problems. I can take a look.

@ryan-bendel
Copy link
Contributor Author

ryan-bendel commented Jul 24, 2025

@ryan-bendel looks like it did get past that problem, but now has ✨other✨ problems. I can take a look.

The index.ts one is odd, as comparing that index to the inject-flags index, the import paths are the same 🤔

Similar confusion with the overrides, i.e override async analyze as they appear to match what the other migrations do 🤷

Thank you for your help! Much appreciated

@thePunderWoman thePunderWoman force-pushed the add-legacyshadowdom-encapsulation branch from 966bc88 to dad506a Compare July 24, 2025 12:02
@thePunderWoman
Copy link
Contributor

@ryan-bendel Bazel can be confusing. I figured out what needed to be in the deps for it to build.

@thePunderWoman thePunderWoman force-pushed the add-legacyshadowdom-encapsulation branch 3 times, most recently from 843b581 to 2c67757 Compare July 24, 2025 12:23
@ryan-bendel
Copy link
Contributor Author

Pushed the test fixes and changes requested, will squash commits when the build is green

@ryan-bendel ryan-bendel force-pushed the add-legacyshadowdom-encapsulation branch 4 times, most recently from cf88059 to bdbd6e8 Compare August 1, 2025 14:12
Copy link
Contributor

@dgp1130 dgp1130 left a comment

Choose a reason for hiding this comment

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

No concerns on my end, thanks for the contribution here!

Copy link
Contributor

@thePunderWoman thePunderWoman left a comment

Choose a reason for hiding this comment

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

LGTM! Super huge thanks, @ryan-bendel, for your effort here! 🎉

reviewed-for: fw-compiler, fw-general, angular-dev, public-api

@thePunderWoman thePunderWoman added the target: minor This PR is targeted for the next minor release label Aug 6, 2025
@thePunderWoman
Copy link
Contributor

@ryan-bendel you have a tiny docs conflict. Once you resolve that, this should be merge ready!!!

Copy link
Member

@JeanMeche JeanMeche left a comment

Choose a reason for hiding this comment

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

reviewed-for: public-api

@thePunderWoman thePunderWoman added the action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews label Aug 6, 2025
IsolatedShadowDom encapsulation fixes style leakage in Shadowdom encapsulation by removing sharedstyleshost from dom-renderer IsolatedShadowdom class. Updates docs.
@ryan-bendel ryan-bendel force-pushed the add-legacyshadowdom-encapsulation branch from bdbd6e8 to d616425 Compare August 6, 2025 12:36
@angular-robot angular-robot bot requested a review from JeanMeche August 6, 2025 12:36
@ryan-bendel
Copy link
Contributor Author

@thePunderWoman merge resolved 😃 - thanks everyone for this

@thePunderWoman thePunderWoman added action: merge The PR is ready for merge by the caretaker and removed action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews labels Aug 6, 2025
@thePunderWoman thePunderWoman removed the request for review from JeanMeche August 6, 2025 13:39
@crisbeto
Copy link
Member

crisbeto commented Aug 6, 2025

This PR was merged into the repository by commit d24d574.

The changes were merged into the following branches: main

@crisbeto crisbeto closed this in d24d574 Aug 6, 2025
@robertIsaac
Copy link
Contributor

oh this is a great feature that we desperately need

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
action: merge The PR is ready for merge by the caretaker area: core Issues related to the framework runtime detected: feature PR contains a feature commit target: minor This PR is targeted for the next minor release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants