How ViewEncapsulation and ng-deep work in Angular

Translation (or rather the original) of my article published by here

Many Angular developers and layout designers who write CSS/SCSS code in Angular applications have encountered a situation where they need to apply styles to a component nested in the current one and, without fully understanding how it works, turned off style encapsulation or added ng-deep, while not taking into account some nuances, which later leads to problems. In this article, I will try to present all the details as simply and concisely as possible.

When a component has style encapsulation enabled (it is enabled by default and should be left enabled in most cases), the styles contained in the component’s style file\files will only apply to the components’ elements. This is very convenient, you don’t have to keep track of the uniqueness of the selectors, you don’t have to use BEM or come up with long class names and keep them unique, although you can still do that if you want. During compilation of the project, Angular itself will add a unique attribute to each element, for example, _ngcontent-ool-c142 and replace your class .my-class on the .my-class[_ngcontent-ool-c142] (this is the case ViewEncapsulation.Emulatedwhich is enabled by default, if you specify `ViewEncapsulation.ShadowDom` the behavior is different but the result is the same).

Now let’s pretend we have a component ComponentA

<div class="checkbox-container">
  <mat-checkbox>Check me</mat-checkbox>
</div>

in which mat-checkbox from Angular material is nested (this can be your own component, not necessarily components from libraries).

There is a label inside the mat-checkbox component,

<mat-checkbox>
	<label>...
</mat-checkbox>

to which we want to add a border. If we write in the component style file

mat-checkbox label {
  border: 1px solid #aabbcc;
}

then after applying ViewEncapsulation.Emulated the selector would be something like this

mat-checkbox[_ngcontent-uiq-c101]   label[_ngcontent-uiq-c101] {
  border: 1px solid #aabbcc;
}

i.e. the border will be applied to the label with the attribute _ngcontent-uiq-c101, but all child elements inside mat-checkbox will have a different attribute, since the label is inside another component, and it will either have an attribute with a different ID (the id of the mat-checkbox component), or it will not exist at all if the component has in turn, encapsulation is disabled. In this case, there will be no attribute at all, because mat-checkbox, like other components from Angular Material ViewEncapsulation.None.

Thus, styles bounded by a component attribute ComponentA only apply to elements directly inside that component. If the component contains another component, then these styles no longer apply to its elements.

If you are interested in exactly how Emulated encapsulation works in Angular, you can find many detailed articles on this topic, but here I will give a very brief description so as not to bloat the article. So, if the component has encapsulation, then the attribute will be added to the component itself _nghost-IDand an attribute will be added to each nested element _ngcontent-ID and all styles in this component will be added to the selector [_ngcontent-ID]. Thus, all styles will be applied ONLY to elements located directly inside this component.

What if we need to apply styles to elements inside a nested component (i.e., in our example, to a label inside a mat-checkbox)

In order to apply styles, we have three options:

  • disable style encapsulation in ComponentA

  • use ng-deep

  • put css code in global styles i.e. styles in styles.(s)css or in other files specified in the styles section of angular.json

Let’s take a closer look at them

Disabling encapsulation

In this case, all styles inside the component will become “global”, and this will happen only after the component has been created, i.e. after the user has visited the section of the application where this component is used, which makes it very difficult to identify this problem. Let’s turn off style encapsulation on our component.

@Component({
  selector: 'app-component-a',
  templateUrl: './component-a.component.html',
  styleUrls: ['./component-a.component.scss'],
  encapsulation: ViewEncapsulation.None
})

remember that in the style file we have

mat-checkbox label {   
  border: 1px solid #aabbcc; 
}

until the user opens the page where the component is used ComponentАall the other mat- checkboxes in the app look unframed, but after ComponentА created, the css code above will be dynamically added to the