How To Prevent Style Encapsulation To Break In Angular
Using stylelint to warn us from accidentally introducing global styles
Style encapsulation is a feature that makes frontend development easier. It reduces the likelihood of introducing a common issue found in web apps: a CSS style declaration intended for one component affecting other unrelated components. By having scoped styles and keeping global styles to a minimum, we reduce potential side effects and keep the necessary styles together with its component.
Surely, there are use cases for having global styles (e.g. a theme) In Angular applications, there are two ways to apply styles globally:
styles.css
: this generated stylesheet will contain global CSS styles applied to the entire application- Avoiding view encapsulation either by disabling it on a component level or by using the custom
::ng-deep
pseudo class
By default, Angular uses emulated encapsulation so that a component’s styles only apply to elements defined in that component’s template. In this mode, the framework generates a unique HTML attribute for each component instance, adds that attribute to elements in the component’s template, and inserts that attribute into the CSS selectors defined in your component’s styles. This mode ensures that a component’s styles do not leak out and affect other components.
However, there are times when styles may need to be overridden, especially when you are dealing with third party components that you cannot control directly. Angular’s emulated encapsulation mode supports a custom pseudo class, ::ng-deep
. Applying this pseudo class to a CSS rule disables encapsulation for that rule, effectively turning it into a global style.
This is a simple example but you can imagine how things will get increasingly complex and hard to debug in bigger projects. In this case, any link will not have an underline which was probably not intended by the developer. To avoid these kind of issues, we want to make it harder to introduce global styles in component style declarations.
Stylelint to the rescue 🚒
Stylelint is a linter for CSS (+ preprocessors like SCSS and LESS) which helps us to avoid errors and encourage consistency in our codebase. You can invoke it in the command line as well as using the IDE plugins for WebStorm / VS Code.
We will leverage the selector-disabled-list rule. The slashes at the beginning and the end are necessary to help stylelint understand that this is a regex. We check whether a selector starts with ::ng-deep
: if it does then stylelint will highlight it for us.
"selector-disallowed-list": [
"/^::ng-deep/",
{
"splitList": true,
"ignore": ["inside-block"]
}
]
This rule does not prevent ::ng-deep
from being used: in an ideal world, you would indeed avoid it at all. But this rule should prevent to use ::ng-deep
as the first selector, thereby breaking view encapsulation and affecting other UI elements by accident. Inside a block it can be fine to use ::ng-deep
as the style should be somewhat scoped.
If necessary the error thrown by stylelint can be silenced with a comment:
/* stylelint-disable-next-line selector-disallowed-list */
::ng-deep body {
background: red;
}
Conclusion
Thanks for reading this short post! View encapsulation is a useful feature in web applications to reduce the accidental leakage of styles. However, it can be easily introduced in Angular applications accidentally which is why it is helpful to let a linter warn us before committing mistakes. Let me know in the comments about your experiences with global style issues.