How ViewEncapsulation and ng-deep work in Angular
Tags: angular, viewencapsulation, css
Published: 2022-05-02 18:07:45


![Keep default Angular ViewEncapsulation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cc5w9u2fft261j65h0pb.png)
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 the creation of each component, Angular itself will add a unique attribute to all elements inside the component, for example, `_ngcontent-ool-c142` and replace your `.my-class` selector with `.my-class[_ngcontent-ool-c142]` (this is in the case of `ViewEncapsulation.Emulated` , which is enabled by default, if you specify `ViewEncapsulation.ShadowDom` the behavior is different but the result is the same).

Now let's imagine that we have a component named `ComponentA` 
```html
<div class="checkbox-container">
  <mat-checkbox>Check me</mat-checkbox>
</div>
```
that has a mat-checkbox from Angular material nested inside it (this can be your own component, not necessarily components from libraries).

Inside mat-checkbox there is a label that we want to add a border to. 

```html
<mat-checkbox>
  <label>
...
```

If we write in the component's style file,

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

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

```css
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 `_ngcontent-uiq-c101` attribute, but all child elements inside the `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 (id of `mat-checkbox` component), or it will not exist at all if the component, in turn, has encapsulation disabled (in our case, there will be no attribute at all, because mat-checkbox, like other components from Angular Material has `ViewEncapsulation.None`).
	Thus, styles restricted by the `ComponentA` component attribute 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're wondering exactly how Angular's Emulated encapsulation works, you can find a lot of detailed articles on the subject, but here I'll give a very brief description so as not to bloat the article. If the component has encapsulation, then the `_nghost-ID` attribute will be added to the component itself, and the `_ngcontent-ID` attribute will be added to each nested element, and `[_ngcontent-ID]` will be added to all styles in this component. This way all styles will be applied ONLY to the elements inside that component.

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

In order to apply styles, we have three options:
- disable style encapsulation in ComponentA 
- use ng-deep 
- place css code in global styles (those in styles.(s)css or in other files specified in the styles section in angular.json) 
 
Let's take a closer look at them

## ViewEncapsulation.None

In this case, all styles inside the component will become "global", and this will happen only after the component is 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.


```typescript
@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 this


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


until the user has opened the page where component A is used, all other mat-checkboxes in the application look borderless, but after component A has rendered, the css code above will be dynamically added to the <style> section in the DOM tree and after then all mat-checkboxes will use these styles. 
In order to prevent this apparently undesirable effect, we can limit the scope of styles by applying a more specific selector. For example, let's add the "checkbox-container" class to the mat-checkbox's parent element, 


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


and fix the selector to this


```css
.checkbox-container mat-checkbox label {
  border: 1px solid #aabbcc;
}
```


now only checkboxes located inside an element with the checkbox-container class will get the border. But instead of adding a class with a unique name and making sure they're not repeatable, it's much easier to use a component selector, because it will be unique


```css
app-component-a mat-checkbox label {
  border: 1px solid #aabbcc;
}
```


**Conclusion: if you turn off encapsulation, don't forget to add the component selector to ALL styles inside the component, in case of SCSS\SASS, just wrap all code in:**


```css
app-component-a {
  ...
}
```


## Pseudo-class ng-deep
 
Now let's turn encapsulation back on by removing encapsulation: `ViewEncapsulation.None` from the `@Component` decorator. And add the `::ng-deep` selector to the css


```css
::ng-deep mat-checkbox label {
  border: 1px solid #aabbcc;
}
```


`ng-deep` will force the framework to generate styles without adding attributes to them, as a result, this code will be added to the DOM:


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


which will affect all mat-checkbox applications, just like if we added it to global styles or turned off encapsulation as we did earlier. To avoid this behavior, we can again restrict the scope to the component selector


```css
::ng-deep app-component-a mat-checkbox label {
  border: 1px solid #aabbcc;
}
```


or do it even simpler and use the `:host` pseudo-class


```css
:host ::ng-deep mat-checkbox label {
  border: 1px solid #aabbcc;
}
```


which is much more convenient and reliable (imagine if you renamed the component selector and forgot to change it in the css code). 
How does it work? Very simple - Angular will generate in this case the following styles


```css
[_nghost-qud-c101] mat-checkbox label{border:1px solid #aabbcc}
```


where `_nghost-qud-c101` is the attribute added to our `ComponentA`, i.e. the border will apply to all labels inside any mat-checkbox lying inside an element with the `_nghost-qud-c101` attribute, which ONLY `ComponentA` has.


```html
<app-component-a _ngcontent-qud-c102 _nghost-qud-c101>
```


**Conclusion: if using ::ng-deep ALWAYS add :host or create a mixin and use it everywhere**


```css
@mixin ng-deep {
  :host ::ng-deep {
    @content;
  }
}

@include ng-deep {
  mat-checkbox label {
    border: 1px solid #aabbcc;
  }
}
```


Many developers are confused by the fact that `ng-deep` has been marked as deprecated for a long time. The Angular team had plans to deprecate this pseudo-class, but that decision was later shelved indefinitely, at least until new alternatives come along. If we compare `ng-deep` and `ViewEncapsulation.None`, then in the first case, we at least turn off encapsulation not for all component styles, but only for those that we need. Even if you have a component where all the styles are for child components, ng-deep seems to be more advantageous because you can later add styles for the component's own elements, in which case you just write them above/below the code nested in `:host ::ng-deep {}` and they will work as usual, but with encapsulation disabled, you no longer have this option.

Finally, I want to add a few words about how to “style” components from libraries. If you need to change the default view for, say, all mat-selects in your application, it's often best to do so in global styles. Sometimes, some developers prefer to put these styles in a separate SCSS file and import it wherever needed, but in this case, when building the project, these styles are duplicated in each chank (compiled js file for a lazy or shared module / group of modules) , where at least one of the components included in this chank uses this style file, which can significantly increase the total size of the bundle. Therefore, this practice should be avoided.