The State of CSS in Angular
Styling applications is a critical part of delivering great experiences for users. Across the web we have Cascading Style Sheets (CSS) as a powerful standard for developers to define the look of an application separately from its construction.
By default in Angular, when you attach CSS directly to a component, we scope that css exclusively to that component. This scoping isolates it from the rest of your application. This additional capability means that there are two ways to use CSS with Angular.
Global CSS — The Way You’re Used To
For as long as CSS has existed, styles loaded on a page have applied globally to every matching element on that page. Most developers are working with global styles like this every day, and the community has developed a myriad of different approach for managing and organizing this, ranging from simple prefixes to more involved systems like BEM and OOCS.
Angular components can be styled via global CSS the same as any other element in your application. Simply drop a `<link>` element on your page (typically in index.html) and you’re good to go! However, Angular additional gives developers more options for scoping your styles.
Component-Scoped CSS
Angular’s component model empowers developers to build in isolation as much as possible. This applies to styles as well, where unexpected side effects from CSS can be undesirable.
We’ve all been in a situation where a seemingly innocuous change, like moving a component to a different location, caused an unexpected cascade of changes. This can happen when insufficiently scoped styles completely throw off a page’s layout. To help avoid experiences like this, Angular will scope a component’s styles such that they only apply within that component’s template.
Three Modes of Component Encapsulation
Developers who use component-scoped CSS in Angular will default to emulated mode for this encapsulation. In emulated mode, we generate and attach random attributes to each instantiated component, and then create styles that use those generated attributes as selectors.
We also support Native mode, which will use Shadow DOM to create additional contexts and prevent cross-component leakage of styles. This will work in any browsers with support for Shadow DOM v1. Native mode creates a shadow root under each component, which will isolate the component completely, even from styles defined globally (global styles DO pierce components with emulated view encapsulation).
The third and last encapsulation you can choose is None, which means that all of your CSS ends up being globally applied. You can use this if you want to opt-out of Angular’s style encapsulation completely.
Read more about Component-Scoped CSS
Deep CSS
But what if I wanted to combine these two worlds? I want to write component-scoped CSS that affects the component itself as well as any children I put inside of it.
Angular and browsers have historically offered what is commonly known as the deep selector. This is also known by several other names, including >>>, /deep/, or the more official Shadow-Piercing descendant combinator.
The deep selector originated as an effort by the Blink team. It allowed developers to style across shadow roots. It was ultimately deprecated as it failed to meet standardization, performance, and developer experience expectations.
Prior to the release of Angular version 4.3, changes in tools such as SASS and other announcements mean that our existing interpretation of /deep/ was creating conflicts. In order to give developers a temporary reprieve from these externally-driven breaking changes, we created ::ng-deep which achieves the same functionality.
As we want to keep our emulated and native style encapsulations as functionality equivalent as possible (with the hope of future browser standardization), we don’t recommend new adoption of /deep/ or ::ng-deep when you can avoid it.
There’s some good coverage of ::ng-deep and the history of this feature in an article by Dor Moshe.
But I want that functionality!
We understand that there are legitimate use cases for component-scoped CSS, where your styles pierce into child components. Here are three ways you can achieve this:
- Use Custom Properties (AKA CSS Variables) — While there isn’t complete browser support for Custom Properties yet, browser implementation of these capabilities will give developers the best combination of isolation and flexibility. Custom Properties, in effect, allow component authors to define an API surface for the styles of their components; they let developers decide what should be styled externally and what needs to stay consistent.
- Use globally-scoped styles sheets and emulated encapsulation — Using traditional CSS you can refer to components by name as part of CSS selectors which will cause styles will be applied as they always have, including piercing down to child components, assuming that you use our emulated (default) view encapsulation on your components.
- Use ::ng-deep — If you need this today, use ::ng-deep. This shouldn’t conflict with any 3rd party tools or new browser development. We are committed to keeping ::ng-deep around until a standards-based approach has achieved industry-wide support.
Community feedback is very important to us. We decided to keep ::ng-deep support until Custom Properties achieve broader acceptance, implementation, and validation from developers. We did this in part thanks to feedback and input from our community. Thank you for your continued support and feedback as we continue to make Angular better and better.