Angular ViewEncapsulation.ShadowDom
By: Gidon Handler
Tags: Angular, JavaScript, CSS, Shadow Dom, HTML

Global Styles

Lets Install Angular, set app.component.ts encapsulation to ShadowDow (as shown above).

We’ll create <h1> tag in the app.component.html file.

And in app.component.scss

h1{color: red;}

and in global style.scss

h1{background-color: green}

As we can, <app-root> is a native web component (shadow-root). Sure is nice not to see the extra attributes on its only tag(<h1>). Its style (color: red) stays in the component and not projected to <head> as in default/emulated mode. But mainly, we see that the global style (background-color: green), which is injected into <head>, does not pierce into the component.

Can we solve this?

Using ::ng-deep to wrap the global style obviously doesn’t work. All ::ng-deep does is write css without the extra attributes in the emulated mode, and the global css doesn’t have those to begin with.

We could do this in every conponent.scss file

@Import "../style.scss";

h1{color: red;}

Import the global css into each and every component. It would work and global style would be available to all shadowDom components, But lets think of a situation where we use Bootstrap. Its about 10,000 lines of global css. And lets say we have a few components on the page, and a list with a hundred list items, and each list item has an icon or two which are also components. We can easily have a thousand or so components on the page and each of those would have a full copy of the bootstrap css. Its way above my pay grade to decide how the browser allocates and treats such situations, and if there are performance implications, but it doesn’t look right.

So we know that global styles can not be used in the template of shadowDow components. This is a problem for using css framworks like Twitter Bootstrap or even Angular material (even though they are a component library, they use global style besides the component style).

Now for the interesting part

What would happen if we create a component with shadowDow and put in its template (which means child) a default or “emulated” component?

Lets first recap. If all components are emulated all the css gets put in the <head> and that’s simple css selectors with the unique [_ngcomponent_xyz] attribute, No problem. If on the other hand, all components are shadowDom, then we can’t use global styles but every component will have its own inner style and will work. But what if a shadow component has an emulated component in its template. The Emulated component gets its style form <head>, but that is blocked by the parent shadow.

Lest create four components.

app.component — shadow parent.component — shadow child.component — shadow grandchild.component — emulated

Now we will create the component tree in exactly that order. Each component has an <h1> tag that is given a color in its component.scss file. We would expect the shadow components to work, but the inner most (grandchild), which is regular/emulated should be blocked form his style in the <head> since he is deep down inside three shadowDom components.

It works! Grandchild.component gets his style. But how?

GrandChild.component does get its style (color: orange) added to <head> (unlike the other shadow components). However he’s not getting its style from the <head> (its there just in case we use it outside of a shadow component). He’s getting its style, because believe it or not, angular inserted his style into each and every shadow component in the module. In the above image, color: orange is only set in grandchild.component.scss file, but we see it in the shadow-root of all the components. In our case, grandchild was in the child.component so it got it from there, his direct parent, but even if we put grandchild in the app.component, angular would still create the same HTML. Just that the grandchild would get his css from app.conponent. Always from his parent.

This was just one line of css. What if there were many css rules and many components. I would argue against combining ShadowDom and emulated components.

Pro question: What would happen if grandchild.component had ViewEncapsulation.none instead of shadow?