Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Conversation

@ryan-bendel
Copy link
Contributor

This fixes an issue where a child component inside of IsolatedShadowDom would get it's styles moved to the root style object, causing it to be unstyled. This new approach hoists the child styles to the shadowroot. This change only applies to IsolatedShadowDom

PR Checklist

Please check if your PR fulfills the following requirements:

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?

After introducing IsolatedShadowDom, it was raised on the PR that any child components using other encapsulation methods would have their styles hoisted to the root styles of the app. This would cause the child component to be unstyled as it is inside a shadowroot.

What is the new behavior?

The new behaviour hoists the styles of any child component into the head of the shadowroot.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Spun up a test project:

  1. Set a global button colour, green.
  2. Created an isolated shadowdom component, with a button, and a child component using emulated, with a button styled red

Comparisons:
Standard/old Shadowdom encapsulation with style leakage:

Screenshot 2025-08-13 at 08 09 09

IsolatedShadowDom pre-fix (styles are moved to root so child component loses styling):

Screenshot 2025-08-13 at 08 14 58

IsolatedShadowDom post-fix (styles are moved to shadowhost, fixing child component styling):
Screenshot 2025-08-13 at 08 09 23

As you can see from the last screenshot, the styles are now correctly applied to the child (Emulated) component

@angular-robot angular-robot bot added detected: feature PR contains a feature commit area: core Issues related to the framework runtime labels Aug 13, 2025
@ngbot ngbot bot added this to the Backlog milestone Aug 13, 2025
@ryan-bendel
Copy link
Contributor Author

ryan-bendel commented Aug 13, 2025

@thePunderWoman et al 👋 - me again!

A follow up to the IsolatedShadowDom PR we did recently, it was raised that there was an issue with child components inside an IsolatedShadowDom component losing their styling (as sharedStylesHost was removed)

I've done a bit of a refactor, providing an alternative way for IsolatedShadowDom to add/hoist styles, which fixes the issue.

This is very much a RFC, or even better if you have capacity to have a quick test? It's a bit of a grey area for me in regards to the shadowdom spec, which doesn't explicitly talk about child component styling (and Angular muddies the waters a bit with different encapsulation methods). However this feels like the best of both worlds, parent styles cannot leak/be added into the shadowdom, but child component styles are correctly applied.

@ryan-bendel ryan-bendel changed the title feat(platform-browser): Fixes IsolatedShadowDom encapsulation method fix(platform-browser): Fixes IsolatedShadowDom encapsulation method Aug 13, 2025
@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch 2 times, most recently from 5039a7e to 367d39d Compare August 13, 2025 10:37
@ryan-bendel ryan-bendel marked this pull request as ready for review August 15, 2025 10:10
@pullapprove pullapprove bot requested a review from AndrewKushnir August 15, 2025 10:11
@AndrewKushnir AndrewKushnir requested review from dgp1130 and thePunderWoman and removed request for AndrewKushnir August 15, 2025 16:40
@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch from bba0484 to 8813776 Compare August 18, 2025 11:38
packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch from 0b8d24e to 42511ea Compare August 19, 2025 13:15
@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch from 42511ea to 19ea9b8 Compare September 13, 2025 06:45
@ryan-bendel
Copy link
Contributor Author

👋 - any chance of getting this merged (stops me having to keep the branch up to date) 😅 ? Would be good to get this in a 21-next release

@alan-agius4 alan-agius4 requested review from crisbeto and removed request for dgp1130 September 13, 2025 07:24
@alan-agius4 alan-agius4 added the action: review The PR is still awaiting reviews from at least one requested reviewer label Sep 13, 2025
@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch from a0c6ebe to 3b5f324 Compare September 13, 2025 08:19
@thePunderWoman
Copy link
Contributor

👋 - any chance of getting this merged (stops me having to keep the branch up to date) 😅 ? Would be good to get this in a 21-next release

We're hoping to take a deeper look at it soon! Thanks for the effort keeping this up to date.

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.

Apologies for the slow review @ryan-bendel, thanks for pushing through this.

Just some minor comments, main thing is just trying to understand the lifecycle of SharedStylesHost and how shadow roots are tracked there.

packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
@ryan-bendel
Copy link
Contributor Author

ryan-bendel commented Sep 20, 2025

@dgp1130 @thePunderWoman - Addressed the comments on the PR. I did encounter a bug though, which meant i've had to do a bit of a refactor (and arguably add a bit more complexity)

I had only tested a single IsolatedShadowDom with child emulated/none components, that worked fine. The issue was, if the same emulated component was introduced elsewhere in the app as well, the styles were hoisted out of the shadowroot, into the app styles, breaking the ui inside IsolatedShadowDom.

My fix now tracks usage, and copies the styles into both app & shadowroot, so styles persist across usages.

The one "gotcha", which I don't think we need to fix (i think it'll exist today) - is if you put a "none" encapsulation component inside a shadowdom, you get into css specificity issues, as there is not a unique identifier in ShadowDom/None like there is with Emulated. Hard to articulate exactly what i mean, hopefully the below image helps. These components are all <p> tags with a color assigned, so IsolatedShadowDom should be blue get's overridden (there's two style's now for p) - I really don't know if there's a fix for this, or even if it should be fixed.

Lastly, i'm unable to really test SSR in practice (i don't really use it so don't feel qualified enough to thoroughly test it) - if one of you would have any capacity to check there's no regressions (even outside of SSR), that would be much appreciated! The more i touch this the less confident i get 🤣

Screenshot 2025-09-20 at 10 01 02

@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch from 1dabb83 to 34391f7 Compare September 22, 2025 15:09
@atscott atscott modified the milestones: Backlog, v21 Candidate Sep 22, 2025
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.

I wouldn't worry too much about SSR given that we don't support declarative shadow DOM just yet. AFAICT, it's just broken today as we seem to put component styles into the <head> tag, which is necessary for initial render but also leaks those styles out of the shadow root.

IsolatedShadowRoot should probably throw when used in SSR until we support declarative shadow DOM. So should ShadowRoot frankly, but I'll check with the rest of the team to see if that's worth the breaking change at the moment.

packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/isolated_style_scope.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/isolated_style_scope.ts Outdated Show resolved Hide resolved
}
}

// Priority 2: Check if element's parent is a shadow root host
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: Why do we care about this case? Shouldn't Element.prototype.getRootNode cover this case as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah this is critical, without it, any child Shadowdom component of Isolated loses it's styles (they're hoisted to Isolated, outside of the scope of the style shadow boundary, so it loses it's styling)

The use case would be Isolated parent, that has a Shadowdom child (with an emulated component projected into the Shadowdom component) - without this, the emulated styles would go to the Isolated (and need to go to the ShadowDom host)

Copy link
Contributor

@dgp1130 dgp1130 Oct 3, 2025

Choose a reason for hiding this comment

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

Sorry, I don't think I'm following this. In my mind, the core logic here should be:

function determineStyleTargets(el: Element): ShadowRoot[] {
  for (const component of walkAncestorComponents(el)) { // Or use `getRootNode` I guess...
    if (usesIsolatedShadowDomEncapsulation(component)) {
      return [component.shadowRoot]; // Apply styles to this one root.
    } else if (usesStandardShadowDomEncapsulation(component)) {
      return allStandardShadowRoots; // Legacy behavior, apply to all legacy roots.
    } else {
      continue; // Move to next ancestor.
    }
  }

  return []; // Not in a shadow root, append to `doc.head`.
}

Is there some combination of IsolatedShadowDom and ShadowDom components for which that doesn't work?

Copy link
Contributor Author

@ryan-bendel ryan-bendel Oct 4, 2025

Choose a reason for hiding this comment

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

I'm probably doing a terrible job of articulating why this is needed tbf as it's quite nuanced (to me at least)! Let me try and break down what I see as happening:

Priority 1 handles projected content (elements in light DOM that are direct children of shadow hosts), while Priority 2 handles elements that are actually inside shadow trees.

When determineStyleTargets gets called:

  • An Emulated or None component needs to apply its styles
  • The renderer calls applyStyles(element) where element is the component's host element
  • Inside applyStyles, it calls this.styleScopeService.determineStyleTargets(element)

What Priority 1 and 2 are checking:
Priority 1: "Is this component a projected direct child of a shadow DOM component?"

Checks if element.parentElement is a shadow root host
This catches: <shadow-component><!-- ng-content --><emulated-component/></shadow-component>

Priority 2: "Is this component inside a shadow DOM tree?"

Checks if element.getRootNode() is a shadow root
This catches components that are deeper inside shadow trees

Projected content has a disconnect between:

Physical DOM location (what getRootNode() sees)
Rendering context (where styles need to be applied)
Priority 1 bridges this gap by checking the parent-child relationship in the light DOM to infer the correct shadow root context for style targeting.

Your suggestion doesn't work because:
It only works for elements that are physically inside shadow roots. It completely misses the projected content case where:

  • Element physically exists in light DOM
  • Element visually renders inside shadow DOM
  • Styles need to target the visual context (shadow root), not the physical context (document)

Priority 1 is essential for content projection directly into an IsolatedShadow, because getRootNode() would return the document instead of the shadow root where styles actually need to be applied - your suggested getRootNode()-only approach fails for projected content because it targets the physical DOM location rather than the visual rendering context.

Copy link
Contributor Author

@ryan-bendel ryan-bendel Oct 4, 2025

Choose a reason for hiding this comment

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

More visually, without the 2 conditions/priorities, this is the use case that breaks:

Screenshot 2025-10-04 at 09 53 34

I don't think we could ship without allowing/fixing the projection into an isolated shadow component as that's a valid use case (I know in my app of ~10,000+ components we will have use cases where we have some very dumb ui component like a card/panel from our ui library using Isolated, but we want to project more complex app specific Emulated components inside of it) - so suspect we'd be opening a can of worms of support tickets without it

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry I think I'm still struggling with this. Maybe a Stackblitz demo of this use case would be helpful to demonstrate why this branch is needed?

It might just be my confused understanding, but there are a few aspects of this which stand out to me as smells:

  1. Checks if element.parentElement is a shadow root host
    This catches: <shadow-component><!-- ng-content --><emulated-component/></shadow-component>

    Content projection may not be a direct child of the host though (ex. <shadow-component><div><!-- ng-content --><emulated-component /><!-- /ng-content --></div></shadow-component>), so I feel like element.parentElement is the wrong mechanism here. At minimum, you probably need to iterate through some framework-level data structures to find the parent component you're being projected into.

  2. Priority 1 handles projected content (elements in light DOM that are direct children of shadow hosts)

    Angular doesn't use native <slot> for content projection though, so how do you end up with light DOM in an IsolatedShadowDom component? All the content should be in the shadow tree, right?

  3. Element physically exists in light DOM
    Element visually renders inside shadow DOM
    Styles need to target the visual context (shadow root), not the physical context (document)

    If the element is in light DOM, don't you need to style that "physical context"? (document.head or an ancestor shadow root?) I would expect to not style the shadow root for content projected from light DOM.

  4. Does this all matter right now if we disable content projection for IsolatedShadowDom as I suggested in another comment? Is this a problem we can solve if/when we get to content projection via native <slot>?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry I think I'm still struggling with this. Maybe a Stackblitz demo of this use case would be helpful to demonstrate why this branch is needed?

It might just be my confused understanding, but there are a few aspects of this which stand out to me as smells:

  1. Checks if element.parentElement is a shadow root host
    This catches: <shadow-component><!-- ng-content --><emulated-component/></shadow-component>

    Content projection may not be a direct child of the host though (ex. <shadow-component><div><!-- ng-content --><emulated-component /><!-- /ng-content --></div></shadow-component>), so I feel like element.parentElement is the wrong mechanism here. At minimum, you probably need to iterate through some framework-level data structures to find the parent component you're being projected into.

  2. Priority 1 handles projected content (elements in light DOM that are direct children of shadow hosts)

    Angular doesn't use native <slot> for content projection though, so how do you end up with light DOM in an IsolatedShadowDom component? All the content should be in the shadow tree, right?

  3. Element physically exists in light DOM
    Element visually renders inside shadow DOM
    Styles need to target the visual context (shadow root), not the physical context (document)

    If the element is in light DOM, don't you need to style that "physical context"? (document.head or an ancestor shadow root?) I would expect to not style the shadow root for content projected from light DOM.

  4. Does this all matter right now if we disable content projection for IsolatedShadowDom as I suggested in another comment? Is this a problem we can solve if/when we get to content projection via native <slot>?

Hey @dgp1130 - Currently laid up with COVID atm so my brain isn't thinking straight, trying to rack my brains of how we could create a stackblitz where we'd be able to fiddle with a local version of the core/platform-browser packages so we can tweak the code and make sure the use cases work and then go from there ... any ideas?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dgp1130 - nevermind, i remembered how to do it 😄 - I've created this stackblitz. You should be able to edit the two packages in the /angular folder. If you comment out that first priority check we were talking about, you'll be able to see what stops working without it.

I think if we didn't style projected content, we'd cause ourselves a headache, as that's a pretty common use case (i know i'll definitely use it with isolated)

I'll hold off addressing the other comments until you've had a chance to have a look and have a fiddle, then we're on the same page about what it's doing. I probably won't get a chance to look till the end of next week

Copy link
Contributor

@dgp1130 dgp1130 Nov 4, 2025

Choose a reason for hiding this comment

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

Currently laid up with COVID atm

Oh no! Hope you feel better soon, take it easy and don't stress out about this too much.

Thanks for that Stackblitz, it really helps I lot. I see how the priority 1 snippet is load-bearing, but I'm not convinced it's the right solution.

Firstly, as I mentioned, you can content project into a nested element, meaning checking the direct parent of the component is not sufficient. The example breaks if you just do:

<isolated-shadow-dom-component>
  <div>
    <button-component />
  </div>
</isolated-shadow-dom-component>

Secondly, I suspect there's a timing bug here. If I call (<button-component>).getRootNode() after rendering, I correctly get the expected shadow root. But if I breakpoint during determineStyleTargets and call (<button-component>).getRootNode(), I get document. Looking at the DOM, it seems that <button-component> is initially rendered in light DOM before somehow being moved into the shadow root.

I'm not very familiar with how content projection works under the hood, so this is a learning experience for me as well, but AFAICT from looking at the generated instructions and breakpointing around, I see that the main component includes:

function App_Template() {
  // ...
  \u0275\u0275elementStart(9, "isolated-shadow-dom-component")(10, "div");
  \u0275\u0275element(11, "button-component");
  \u0275\u0275elementEnd()();
  // ...
}

This appears to render <isolated-shadow-dom-component><div><button-component></button-component></div></isolated-shadow-dom-component> (all in light DOM) and is triggered here. Afterwards, we render component children to expand <isolated-shadow-dom-component> itself. This executes its template:

function ShadowDomComponent_Template(rf, ctx) {
  \u0275\u0275projectionDef();
  \u0275\u0275text(0, "Projected content:\n");
  \u0275\u0275domElementStart(1, "h4");
  \u0275\u0275text(2, "This will be green (from global styles)");
  \u0275\u0275domElementEnd();
  \u0275\u0275projection(3);
},

That last \u0275\u0275projection(3); call appears to move the element into the shadow root to its final position.

I feel like the fundamental issue here is that we're attempting to apply styles based on an intermediate state of the DOM. Ideally, we would render the element initially within the shadow root so it is always in a correct position, though that seems complicated from a timing perspective, since the parent element performing the projection renders before the child component has rendered the container element it will ultimately be projected into. I suspect the easier solution is to change the timing of when we apply styles to happen after the full component is fully rendered into its final position, then we could probably get rid of this "priority 1" case and .getRootNode() should return the desired ShadowRoot 100% of the time.

Third point is that I'm still not convinced content projection into a shadow root is a good idea in general or that we should propagate that design decision into IsolatedShadowRoot. In conversation with @jelbourn, it seems that you can apparently use native <slot> in the framework today. Our current thinking is that it's actually better to disable <ng-content> altogether for IsolatedShadowDom and point users are native <slot> instead. There's definitely a conceptual gap between those two concepts and this is definitely a non-trivial breaking change between ViewEncapsulation.ShadowDom and ViewEncapsulation.IsolatedShadowDom, but should leave us in a position closer to the web standard. From a styling perspective, this should make our job easier, as projected content will always be in the light DOM of its target component, so element.getRootNode() should return the correct value (once we figure out the timing issue above). Actually, I'd be curious if this fixes the timing issue because we wouldn't go through \u0275\u0275projection at all. None or Emulated components also won't leak their styles into shadow roots they happen to be projected into, which provides better isolation.

Does all that sound reasonable? Am I understanding the issue "priority 1" was supposed to address?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dgp1130 - i think i'm with you

I've taken a stab at implementing this, stackblitz here

It's meant a few changes to a couple of packages which are hard to see from the compiled code, but essentially it was a change in projection.ts to the ɵɵprojectionDef function (this throws the console error)

And a change to handler.ts in compiler-cli to give us the nice dialog/compilation style error

Slot works, ng-content now throws an error

The other thing i've done is refactored the isolated_style_scope service to get rid of that priority check now we're using only slot as you suggested, and to use a load of framework helper functions i stumbled across in core (lview stuff essentially, namely the getLContext unwrapRNode HOST and PARENT)

I've not looked at if you start trying to project content inside a component that is in turn projected into isolated, as that's a rabbit hole to go down, maybe something we can add later (or someone from the team who is a lot more knowledgeable of the framework than I! 😆 )

packages/platform-browser/src/dom/isolated_style_scope.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/isolated_style_scope.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/isolated_style_scope.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/isolated_style_scope.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/isolated_style_scope.ts Outdated Show resolved Hide resolved
@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch from 39be8de to f0a0dc5 Compare October 3, 2025 09:42
@ryan-bendel ryan-bendel requested a review from dgp1130 October 3, 2025 10:36
@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch from 7755745 to f8deb43 Compare October 3, 2025 11:45
packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/isolated_style_scope.ts Outdated Show resolved Hide resolved
@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch from 9f3ae01 to a4d94cd Compare October 4, 2025 08:18
@dgp1130
Copy link
Contributor

dgp1130 commented Oct 10, 2025

Thanks for the follow up, I haven't had a chance to look at it just yet, but FYI, I'm gonna be out for a couple weeks and will look at this when I get back.

packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
this.shadowRoot = (hostEl as HTMLElement).attachShadow({mode: 'open'});
} else {
// In SSR or environments without shadow DOM support, throw
throw new Error(
Copy link
Contributor

Choose a reason for hiding this comment

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

Bug: Won't this throw for standard ViewEncapsulation.ShadowDom? I think we need to maintain the existing behavior and only throw for IsolatedShadowDom.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dgp1130 - maybe i'm misremembering, but standard Shadowdom doesn't work in SSR either, right? Which is what this would catch. I could put in a condition to only throw for Isolated, but i'm not clear on what standard would do in a server environment here....

packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/isolated_style_scope.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/shared_styles_host.ts Outdated Show resolved Hide resolved
@JeanMeche
Copy link
Member

Looks like I found a usecase on angular.dev for that feature once the fix lands
By any change could you rebase the PR on main and take into account Doug's comments.
Thank you.

@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch 2 times, most recently from 1439753 to 94d7b2e Compare December 1, 2025 07:45
@ryan-bendel
Copy link
Contributor Author

ryan-bendel commented Dec 1, 2025

Looks like I found a usecase on angular.dev for that feature once the fix lands By any change could you rebase the PR on main and take into account Doug's comments. Thank you.

@JeanMeche @dgp1130

I've refactored IsolatedShadowDom now to use native slot and disallow ng-content as discussed. This definitely simplified things and fixes any wrapped child/projected content issues.

Removed dom walking in isolated_style_scope and replaced with using Angulars LView hierarchy which is much nicer.

Demo if you want to have play (if you uncomment ng-content it should generate an error)

@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch from 032f709 to 8530d0e Compare December 1, 2025 09:04
packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
packages/platform-browser/test/dom/dom_renderer_spec.ts Outdated Show resolved Hide resolved
"dist/main.js": 153915,
"dist/polyfills.js": 34023,
"dist/open-close.component-[hash].js": 1190
"dist/main.js": 159053,
Copy link
Member

Choose a reason for hiding this comment

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

Since this is quite an increase, we should investigate better tree shaking when the feature isn't used

Copy link
Contributor Author

@ryan-bendel ryan-bendel Dec 1, 2025

Choose a reason for hiding this comment

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

I shall take a look @JeanMeche - but i am struggling to run the integration tests locally (i bumped all the size.json from the output in the gh test runner output),

I get:

ERROR  Failed to switch pnpm to v10.23.0. Looks like pnpm CLI is missing at "/var/folders/kj/cqmyhbyx0zv7mrtvpdlj19dw0000gp/T/ng-integration-test7nTJJM/tmp-env-0/Library/pnpm/.tools/pnpm/10.23.0/bin" or is incorrect
spawnSync /var/folders/kj/cqmyhbyx0zv7mrtvpdlj19dw0000gp/T/ng-integration-test7nTJJM/tmp-env-0/Library/pnpm/.tools/pnpm/10.23.0/bin/pnpm ENOENT
Command failed: `pnpm install`

🤔

@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch from 2c2deab to 60acf6d Compare December 1, 2025 09:40
packages/core/src/render3/instructions/projection.ts Outdated Show resolved Hide resolved
packages/core/src/render3/instructions/projection.ts Outdated Show resolved Hide resolved
packages/platform-browser/src/dom/dom_renderer.ts Outdated Show resolved Hide resolved
@ryan-bendel ryan-bendel force-pushed the fix-isolated-shadowdom branch from 7cdd447 to c0d2fb9 Compare December 1, 2025 11:52
Implement IsolatedStyleScopeService. Refactor IsolatedShadowDom implementation to fix various bugs. Uses native slot instead of ng-content.
@dgp1130
Copy link
Contributor

dgp1130 commented Dec 13, 2025

Sorry for the slow responses on my end, I've been meaning to take another look here but have just been held up.

FYI, I made a related PR #66048 to fix bootstrapping Angular inside of a shadow root and found myself rewriting most of the styling system to better support ViewEncapsulation.IsolatedShadowDom and incorporating many of the ideas from our conversation in this PR. When I take another look, I'll try to figure out if it's still worth landing this PR independently and potentially rebasing mine on top of it (or vice versa depending on which lands first).

@dgp1130
Copy link
Contributor

dgp1130 commented Dec 20, 2025

Looking at this PR again, I'm thinking the changes to shared_styles_host and dom_renderer and mostly obsoleted by #66048 which achieves much of the same result (unless you see something I'm not addressing there).

Where there is still value is in the changes around that. Specifically the errors for running IsolatedShadowDom in SSR or using <ng-content /> with it, both of which are not included in my PR and are worth landing independently. I'm thinking it might be worth closing this PR and splitting into a couple others for those more specific features and potentially more tests for the new constraints of IsolatedShadowDom which I probably did not exhaustively cover.

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

Labels

action: review The PR is still awaiting reviews from at least one requested reviewer area: core Issues related to the framework runtime detected: feature PR contains a feature commit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.