Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,3 @@ import { Component } from '@angular/core';
})
export class QuestSummaryComponent { }
// #enddocregion
/*
// #docregion encapsulation.shadow
// warning: not all browsers support shadow DOM encapsulation at this time
encapsulation: ViewEncapsulation.ShadowDom
// #enddocregion encapsulation.shadow
*/
Comment on lines 13 to 18
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've removed this because as mentioned in this comment I feel like it is an unnecessary example

95 changes: 43 additions & 52 deletions aio/content/guide/view-encapsulation.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,37 @@
# View encapsulation

In Angular, component CSS styles are encapsulated into the component's view and don't
affect the rest of the application.
In Angular, a component's styles can be encapsulated within the component's host element so that they don't affect the rest of the application.

The `Component`'s decorator provides the [`encapsulation`](api/core/Component#encapsulation) option which can be used to control how the encapsulation is applied on a _per component_ basis.

To control how this encapsulation happens on a _per
component_ basis, set the _view encapsulation mode_ in the component metadata.
Choose from the following modes:

- `ShadowDom` view encapsulation uses the browser's built-in shadow DOM implementation (see
[Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM))
to attach a shadow DOM to the component's host element, and then puts the component
view inside that shadow DOM. The component's styles are included within the shadow DOM.
- `ViewEncapsulation.ShadowDom`, Angular uses the browser's built-in [Shadow DOM API](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)) to enclose the component's view inside a ShadowRoot (used as the component's host element) and apply the provided styles in an isolated manner.

- `ViewEncapsulation.Emulated`, Angular modifies the component's CSS selectors so that they are only applied to the component's view and do not affect other elements in the application (_emulating_ Shadow DOM behavior). For more details, see [Inspecting generated CSS](guide/view-encapsulation#inspect-generated-css).

- `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing
(and renaming) the CSS code to effectively scope the CSS to the component's view.
For details, see [Inspecting generated CSS](guide/view-encapsulation#inspect-generated-css).
- `ViewEncapsulation.None`, Angular does not apply any sort of view encapsulation meaning that any styles specified
for the component are actually globally applied and can affect any HTML element present within the application.
This mode is essentially the same as including the styles into the HTML itself.

- `None` means that Angular does no view encapsulation.
Angular adds the CSS to the global styles.
The scoping rules, isolations, and protections discussed earlier don't apply.
This mode is essentially the same as pasting the component's styles into the HTML.
<div class="alert is-important">
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

maybe it should be is-helpful? 🤷‍♂️

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Don't all browsers that we officially support have ShadowDOM support now?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the real reason we are still defaulting to emulated is that is makes it hard to pierce theming into the components because there is not a well lit path for how to do this with ShadowDOM. I think @jelbourn has more context here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes I wrote something similar in my comment below, happy to change this to whatever is preferred 🙂

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this is fine unless @jelbourn (or anyone else has a view).


To set the component's encapsulation mode, use the `encapsulation` property in the component metadata:
`ViewEncapsulation.ShadowDom` only works on browsers that have built-in support
for the shadow DOM (see [Can I use - Shadow DOM v1](https://caniuse.com/shadowdomv1)).
Not all browsers support it, which is why the `ViewEncapsulation.Emulated` is the recommended and default mode.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I changed "The support is still limited" to "Not all browsers support it" because as you can see in the caniuse link the support is actually pretty good I think


<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.shadow" header="src/app/quest-summary.component.ts"></code-example>
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've removed the example as it didn't seem beneficial to me as:

  • after my changes the introductory text above already makes clear how to apply the strategy so I don't really think actually showing it adds much here
  • in the same docs page (below) there are various examples showing the application of the encapsulation strategy, so this example again didn't seem to add much to me

</div>

`ShadowDom` view encapsulation only works on browsers that have built-in support
for shadow DOM (see [Can I use - Shadow DOM v1](https://caniuse.com/shadowdomv1)).
The support is still limited, which is why `Emulated` view encapsulation is the default mode and recommended in most cases.

{@a inspect-generated-css}

## Inspecting generated CSS

When using emulated view encapsulation, Angular preprocesses
all component styles so that they approximate the standard shadow CSS scoping rules.
When using the emulated view encapsulation, Angular pre-processes all the component's styles so that they are only applied to the component's view.

In the DOM of a running Angular application with emulated view
encapsulation enabled, each DOM element has some extra attributes
attached to it:
In the DOM of a running Angular application, elements belonging to components using emulated view encapsulation have some extra attributes attached to them:

<code-example format="">
<code-example format="html" language="html">
&lt;hero-details _nghost-pmm-5>
&lt;h2 _ngcontent-pmm-5>Mister Fantastic&lt;/h2>
&lt;hero-team _ngcontent-pmm-5 _nghost-pmm-6>
Expand All @@ -49,18 +40,16 @@ attached to it:
&lt;/hero-detail>
</code-example>

There are two kinds of generated attributes:
There are two kinds of such attributes:

- `_nghost` attributes are added to elements that enclose a component's view and that would be ShadowRoots in a native Shadow DOM encapsulation. This is typically the case for components' host elements.
- `_ngcontent` attributes are added to child element within a component's view, those are used to match the elements with their respective emulated ShadowRoots (host elements with a matching `_nghost` attribute).

- An element that would be a shadow DOM host in native encapsulation has a
generated `_nghost` attribute. This is typically the case for component host elements.
- An element within a component's view has a `_ngcontent` attribute
that identifies to which host's emulated shadow DOM this element belongs.
The exact values of these attributes are a private implementation detail of Angular. They are automatically generated and you should never refer to them in application code.

The exact values of these attributes aren't important. They are automatically
generated and you should never refer to them in application code. But they are targeted
by the generated component styles, which are in the `<head>` section of the DOM:
They are targeted by the generated component styles, which are injected in the `<head>` section of the DOM:

<code-example format="">
<code-example format="css" language="css">
[_nghost-pmm-5] {
display: block;
border: 1px solid black;
Expand All @@ -72,25 +61,29 @@ h3[_ngcontent-pmm-6] {
}
</code-example>

These styles are post-processed so that each selector is augmented
with `_nghost` or `_ngcontent` attribute selectors.
These extra selectors enable the scoping rules described in this page.
These styles are post-processed so that each CSS selector is augmented with the appropriate `_nghost` or `_ngcontent` attribute. These modified selectors make sure the styles to be applied to components' views in an isolated and targeted fashion.

## Mixing encapsulation modes

Avoid mixing components that use different view encapsulation. Where it is necessary, you should be aware of how the component styles will interact.
As previously mentioned you specify the encapsulation mode in the Component's decorator on a _per component_ basis, this means that within your application you can have different components using different encapsulation strategies.

Although possible, this is not recommended. If it is really needed you should be aware of how the styles of components using different encapsulation modes will interact with each other:

- The styles of components with `ViewEncapsulation.Emulated` are added to the `<head>` of the document, making them available throughout the application, but their selectors only affect elements within their respective components' templates.

- The styles of components with `ViewEncapsulation.None` are added to the `<head>` of the document, making them available throughout the application, so are completely global and affect any matching elements within the document.

- The styles of components with `ViewEncapsulation.Emulated` are added to the `<head>` of the document, making them available throughout the application, but are "scoped" so they only affect elements within the component's template.
- The styles of components with `ViewEncapsulation.ShadowDom` are only added to the shadow DOM host, ensuring that they only affect elements within their respective components' views.

- The styles of components with `ViewEncapsulation.None` are added to the `<head>` of the document, making them available throughout the application, and are not "scoped" so they can affect any element in the application.
<div class="alert is-helpful">
Comment thread
dario-piotrowicz marked this conversation as resolved.

- The styles of components with `ViewEncapsulation.ShadowDom` are only added to the shadow DOM host, ensuring that they only affect elements within the component's template.
Styles of `ViewEncapsulation.Emulated` and `ViewEncapsulation.None` components are also added to the shadow DOM host of each `ViewEncapsulation.ShadowDom` component.

**All the styles for `ViewEncapsulation.Emulated` and `ViewEncapsulation.None` components are also added to the shadow DOM host of each `ViewEncapsulation.ShadowDom` component.**
This means that styles for components with `ViewEncapsulation.None` will affect matching elements within shadow DOMs.

The result is that styling for components with `ViewEncapsulation.None` will affect matching elements within the shadow DOM.
This approach may seem counter-intuitive at first, but without it a component with `ViewEncapsulation.None` would be rendered differently within a component using `ViewEncapsulation.ShadowDom`, since its styles would not be available.

This approach may seem counter-intuitive at first, but without it a component with `ViewEncapsulation.None` could not be used within a component with `ViewEncapsulation.ShadowDom`, since its styles would not be available.
</div>

### Examples

Expand All @@ -106,7 +99,7 @@ The first example shows a component that has `ViewEncapsulation.None`. This comp

Angular adds the styles for this component as global styles to the `<head>` of the document.

**Angular also adds the styles to all shadow DOM hosts.** Therefore, the styles are available throughout the application.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this was already mentioned above so I don't think there is a need to emphasise it

As already mentioned Angular also adds the styles to all shadow DOM hosts. Therefore, the styles are available throughout the whole application.

<img src="generated/images/guide/view-encapsulation/no-encapsulation.png" alt="component with no encapsulation">

Expand All @@ -116,14 +109,12 @@ The second example shows a component that has `ViewEncapsulation.Emulated`. This

<code-example path="view-encapsulation/src/app/emulated-encapsulation.component.ts" header="src/app/emulated-encapsulation.component.ts"></code-example>>

Similar to `ViewEncapsulation.None`, Angular adds the styles for this component to the `<head>` of the document, and to all the shadow DOM hosts.
But in this case, the styles are "scoped" by the attributes described in ["Inspecting generated CSS"](#inspecting-generated-css).
Similar to `ViewEncapsulation.None`, Angular adds the styles for this component to the `<head>` of the document, but with "scoped" styles.

Therefore, only the elements directly within this component's template will match its styles.
Since the "scoped" styles from the `EmulatedEncapsulationComponent` are very specific, they override the global styles from the `NoEncapsulationComponent`.

In this example, the `EmulatedEncapsulationComponent` contains a `NoEncapsulationComponent`.
The `NoEncapsulationComponent` is styled as expected because the scoped styles do not match elements in its template.
In this example, the `EmulatedEncapsulationComponent` contains a `NoEncapsulationComponent`, but `NoEncapsulationComponent` is still styled as expected since the `EmulatedEncapsulationComponent`'s "scoped" styles do not match elements in its template.

<img src="generated/images/guide/view-encapsulation/emulated-encapsulation.png" alt="component with no encapsulation">

Expand All @@ -144,8 +135,8 @@ The styles added by the `ShadowDomEncapsulationComponent` component are availabl
The `EmulatedEncapsulationComponent` has specific "scoped" styles, so the styling of this component's template is unaffected.

But since styles from `ShadowDomEncapsulationComponent` are added to the shadow host after the global styles, the `h2` style overrides the style from the `NoEncapsulationComponent`.
The result is that the `<h2>` element in the `NoEncapsulationComponent` is colored blue rather than red, which may not be what the component author intended.
The result is that the `<h2>` element in the `NoEncapsulationComponent` is colored blue rather than red, which may not be what the component's author intended.

<img src="generated/images/guide/view-encapsulation/shadow-dom-encapsulation.png" alt="component with no encapsulation">

@reviewed 2021-09-17
@reviewed 2021-11-10
2 changes: 1 addition & 1 deletion packages/compiler/src/render3/partial/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export interface R3DeclareComponentMetadata extends R3DeclareDirectiveMetadata {
changeDetection?: ChangeDetectionStrategy;

/**
* An encapsulation policy for the template and CSS styles.
Comment thread
dario-piotrowicz marked this conversation as resolved.
Outdated
* An encapsulation policy for the component's styling.
* Defaults to `ViewEncapsulation.Emulated`.
*/
encapsulation?: ViewEncapsulation;
Expand Down
11 changes: 6 additions & 5 deletions packages/compiler/src/render3/view/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,12 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
styles: string[];

/**
* An encapsulation policy for the template and CSS styles. One of:
* - `ViewEncapsulation.Emulated`: Use shimmed CSS that emulates the native behavior.
* - `ViewEncapsulation.None`: Use global CSS without any encapsulation.
* - `ViewEncapsulation.ShadowDom`: Use the latest ShadowDOM API to natively encapsulate styles
* into a shadow root.
* An encapsulation policy for the component's styling.
* Possible values:
* - `ViewEncapsulation.Emulated`: Apply modified component styles in order to emulate
* a native Shadow DOM CSS encapsulation behavior.
* - `ViewEncapsulation.None`: Apply component styles globally without any sort of encapsulation.
* - `ViewEncapsulation.ShadowDom`: Use the browser's native Shadow DOM API to encapsulate styles.
*/
encapsulation: ViewEncapsulation;

Expand Down
25 changes: 13 additions & 12 deletions packages/core/src/metadata/directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,18 +517,19 @@ export interface Component extends Directive {
animations?: any[];

/**
* An encapsulation policy for the template and CSS styles. One of:
* - `ViewEncapsulation.Emulated`: Use shimmed CSS that
* emulates the native behavior.
* - `ViewEncapsulation.None`: Use global CSS without any
* encapsulation.
* - `ViewEncapsulation.ShadowDom`: Use Shadow DOM v1 to encapsulate styles.
*
* If not supplied, the value is taken from `CompilerOptions`. The default compiler option is
* `ViewEncapsulation.Emulated`.
*
* If the policy is set to `ViewEncapsulation.Emulated` and the component has no `styles`
* or `styleUrls` specified, the policy is automatically switched to `ViewEncapsulation.None`.
* An encapsulation policy for the component's styling.
* Possible values:
* - `ViewEncapsulation.Emulated`: Apply modified component styles in order to emulate
* a native Shadow DOM CSS encapsulation behavior.
* - `ViewEncapsulation.None`: Apply component styles globally without any sort of encapsulation.
* - `ViewEncapsulation.ShadowDom`: Use the browser's native Shadow DOM API to encapsulate styles.
*
* If not supplied, the value is taken from the `CompilerOptions`
* which defaults to `ViewEncapsulation.Emulated`.
*
* If the policy is `ViewEncapsulation.Emulated` and the component has no
* {@link Component#styles styles} nor {@link Component#styleUrls styleUrls},
* the policy is automatically switched to `ViewEncapsulation.None`.
*/
encapsulation?: ViewEncapsulation;

Expand Down
22 changes: 11 additions & 11 deletions packages/core/src/metadata/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
*/

/**
* Defines template and style encapsulation options available for Component's {@link Component}.
* Defines the CSS styles encapsulation policies for the {@link Component} decorator's
* `encapsulation` option.
*
* See {@link Component#encapsulation encapsulation}.
*
Expand All @@ -23,10 +24,9 @@ export enum ViewEncapsulation {
// https://github.com/angular/angular/issues/44119 for additional information.

/**
* Emulate `Native` scoping of styles by adding an attribute containing surrogate id to the Host
* Element and pre-processing the style rules provided via {@link Component#styles styles} or
* {@link Component#styleUrls styleUrls}, and adding the new Host Element attribute to all
* selectors.
* Emulates a native Shadow DOM encapsulation behavior by adding a specific attribute to the
* component's host element and applying the same attribute to all the CSS selectors provided
* via {@link Component#styles styles} or {@link Component#styleUrls styleUrls}.
*
* This is the default option.
*/
Expand All @@ -35,16 +35,16 @@ export enum ViewEncapsulation {
// Historically the 1 value was for `Native` encapsulation which has been removed as of v11.

/**
* Don't provide any template or style encapsulation.
* Doesn't provide any sort of CSS style encapsulation, meaning that all the styles provided
* via {@link Component#styles styles} or {@link Component#styleUrls styleUrls} are applicable
* to any HTML element of the application regardless of their host Component.
*/
None = 2,

/**
* Use Shadow DOM to encapsulate styles.
*
* For the DOM this means using modern [Shadow
* DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM) and
* creating a ShadowRoot for Component's Host Element.
* Uses the browser's native Shadow DOM API to encapsulate CSS styles, meaning that it creates
* a ShadowRoot for the component's host element which is then used to encapsulate
* all the Component's styling.
*/
ShadowDom = 3
}