202

I'm trying to style an element placed by the router outlet in angular and want to make sure that the element generated gets a width of 100%

From most of the replies, I'm seeing that I should use the ::ng-deep selector, but from Angular's docs it is being deprecated. Is there an alternative to ::ng-deep?

Aaqib
  • 9,942
  • 4
  • 21
  • 30
Jacob Schwartz
  • 2,195
  • 2
  • 13
  • 9
  • 8
    `::ng-deep` isn't going anywhere. It will always be a setting you can enable. There is absolutely no way they can remove it now without massive community backlash. Look at how many results come back for this search https://github.com/search?q=%3A%3Ang-deep&type=Code - it's like saying the css `!important` property is going to disappear – Simon_Weaver Feb 20 '19 at 05:09
  • I don't know -- I did a project-wide search out of curiosity in our mono-repo (multiple fairly large enterprise apps) and only came out with 69 references. I feel like that's definitely an acceptable refactor to move out of deprecation and would gladly do it whenever they bring out the alternative. Besides, `!important` has an important place in the CSS spec whereas `::deep` was always only a proposal. – dudewad May 10 '19 at 16:46

7 Answers7

213

FWIW In my research I have not found any replacement for ng-deep or the other applicable alternatives. This is because, I believe, the Angular team is deferring to the W3C spec on the shadow dom, which initially had selectors such as deep. However, the W3c has since removed the recommendation, but not replaced it with a new one. Until that happens, I imagine that the Angular team will keep ::ng-deep and it's alternatives available, but in deprecated state due to the pending state of W3C's drafts. I am not able to take the time to find the documentation to back this up right now but I did see it recently.

Long story short: Keep using ::ng-deep and its alternatives until a replacement is created - the deprecation is just an early notice so that people aren't blindsided whenever the actual change materializes.

-- UPDATE --

https://drafts.csswg.org/css-scoping-1/ Here is the draft proposal if you're interested. It appears that they are working on a robust set of selectors for elements within a shadow dom tree; it is this spec, once approved, that I think will inform the angular clone, if there even is one (i.e. angular may not need to implement their own selectors once this goes live in browsers).

dudewad
  • 13,215
  • 6
  • 37
  • 46
  • I agree with this, but I would not recommend writing new code purposefully using deprecated framework (and browser) functionality. – MT_ Jun 11 '18 at 19:47
  • 8
    Either would I. But there is no alternative, which I think is failrly clearly outlined here. Do you have any suggestions to help with that? – dudewad Jun 12 '18 at 18:17
  • 1
    The only alternative I can quickly think of would be refactoring the nesting of components, which may be more work than you have time for but could yield other benefits... – MT_ Jun 13 '18 at 16:22
  • Indeed. But that's not the question at hand :) – dudewad Jun 13 '18 at 20:17
  • 75
    With a third party library it's virtually impossible to avoid having to use `::ng-deep` once in a while (if you care at all about how your site looks) - even with something like angular material. They have bugs that aren't fixed for months, and workarounds often involve ng-deep. And don't confuse the different deprecated 'deep' selectors - `::ng-deep` is definitely the least deprecated one. – Simon_Weaver Feb 20 '19 at 05:04
  • I feel you; I actualy avoid angular material for that exact reason. I constantly have to use ::ng-deep with it probably an order of magnitude more than anywhere else in my applications. – dudewad Feb 20 '19 at 20:02
  • @MT_: I have another use-case: I want to define a common control or reusable component. It has a set of default styles and will be used in a lot of places in the application. Some times, from a parent, I need to adjust it to just view differently. How would I do this? To me, start adding 'classes input' or tailoring special styles driven by a single scenario is a poor design choice. – mdarefull Mar 12 '19 at 20:25
  • 2
    Yeah this is one of the ugliest parts of the entire system. But encapsulation is what encapsulation is. You have to break the boundary either by explicitly using ::ng-deep in css, or you need to do it programatically. We sometimes use an attribute on the component tag to indicate what "mode" a component is in (i.e. context), and then the styles can live in the child component w/o ::ng-deep via an attribute selector like: `:host[some-context] {}` - it depends on what kind of flexibility/portability you want. I don't super like either way but this is the world of encapsulation. – dudewad Mar 12 '19 at 23:01
  • 2
    There is an ongoing discussion: [Clarify information about deprecation of :ng-deep and recommend replacement](https://github.com/angular/angular/issues/25160) and a recent blog-post with a statement about [Future of overriding styles](https://blog.angular.io/the-new-state-of-css-in-angular-bec011715ee6#12af) – kapsiR Nov 25 '21 at 13:50
  • 1
    Deprecation without a replacement is bad form. – cambunctious Nov 14 '22 at 22:41
  • @cambunctious I don't think it is. Deprecation means "this thing is going to go away at an undetermined time and we don't know when or necessarily how that will happen". Not having a replacement is sometimes part of that. As mentioned here, the Angular team is deferring to the W3C proposal that defined a ::deep selector and then pulled it, leaving things in limbo. So, its not really on the Angular team, as they are doing the best thing and mirroring the actual web specifications. – dudewad Nov 15 '22 at 04:45
  • 1
    I disagree. Deprecation means "this is not recommended because something more preferable is now available". – cambunctious Nov 15 '22 at 19:05
  • That seems like a narrow definition, but you can take it up with the W3C if you'd like ‍♂️ – dudewad Nov 16 '22 at 01:27
85

The simple and easy alternative to a deep style is a common style using the element selector of the parent component. So if you had this in hero-details.component.css:

:host ::ng-deep h3 {
  font-style: italic;
}

It would become this in styles.css:

app-hero-details h3 {
  font-style: italic;
}

Basically a deep style is an un-encapsulated style so it conceptually seems more like a common style to me than a component style. Personally I would not use deep styles anymore. Breaking changes are normal in major version updates and deprecated feature removal is fair game.

J. Lenthe
  • 1,330
  • 10
  • 10
  • 4
    Wow, I feel dumb now. Thanks! Coming from other front-end frameworks I thought this was impossible – Rafael Vidaurre Aug 21 '20 at 18:50
  • 2
    This is really useful. It's a pitty that ::ng-deep was deprecated for so long without having a replacement ( :host::ng-deep works as expected, but I do not want to use deprecated stuff). – Alexei - check Codidact Oct 01 '20 at 16:00
  • 1
    I am confused, how should this work? If you spcify the `app-hero-details h3 { ... }` in the parent, it gets encapsulated and won't affect the h3 in the children component. If you're suggesting to use this in general shared styles, then ok, but it's not it should be done I believe. – Tony Sep 15 '21 at 14:46
  • 1
    This solution eliminates benefits from organization, lazy loading etc. AliF50's solution is preferable imo – adamdport Oct 06 '21 at 15:55
  • @adamdport AliF50's solution cause styles to leak into other components. – ytan11 May 19 '22 at 02:36
  • @ytan11 right, that's also what `ng-deep` does, which the original question asked for an alternative to – adamdport May 19 '22 at 13:09
  • This is not working i have xterm (terminal) component and i need to update some styles from Parent to apply 2 different styles based on usages? updating styles from parent doesn't work neither from inside shared component IT only works when used `:host ng::deep STYLES` – khizer May 24 '22 at 06:10
  • same for me, this does not work – Amir Choubani Feb 21 '23 at 16:03
  • How did I not know this! Been searching for a way out of the deprecated ::ng-deep for what feels like forever – tomcheney Apr 04 '23 at 13:33
33

To bypass the deprecated ::ng-deep, I usually disable ViewEncapsulation. Although this is not the best approach, it has served me well.

To disable ViewEncapsulation, do the following in your component:

import { Component, ViewEncapsulation } from '@angular/core';

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

export class HeaderComponent {

}

This will make the .scss styles in this component global to the whole application. To not allow the styles to go up the chain to parent and sibling components, wrap the whole scss with the selector like so:

app-header {
  // your styles here and any child component styles can go here
}

Now, the styles specified here will go down to children components so you have to be extra specific with your css selectors and mind your p's and q's when adding CSS (maybe add the child selector specified in your Angular app and then its styles).

I say it is not the best approach because of the paragraph above, but this has served me well.

frido
  • 13,065
  • 5
  • 42
  • 56
AliF50
  • 16,947
  • 1
  • 21
  • 37
  • 33
    This is just workaround and if you have huge project, switching off `ViewEncapsulation` will make a lot of damage by making those styles possible to leak into all components. [This feature should be used wisely and with full understanding](https://dzone.com/articles/what-is-viewencapsulation-in-angular) – mpro Jan 02 '19 at 15:54
  • 6
    @mpro I understand, that's why I gave the caveat and say this is not the best approach and you have to mind your p's and q's and have to be extra specific. For me, this approach has worked well so far. ::ng-deep is marked for deprecation and this is a workaround. – AliF50 Jan 02 '19 at 21:34
  • 2
    Honestly I think this is a horrible conclusion to reach if you did this because of threatened deprecation. Yes I know you acknowledges as much, but I really think you're shooting yourself in the foot by doing this. View encapsulation is so useful for many reasons. However it's not nearly as bad as whoever on the angular team deprecated it without any logical workaround and leading many to much confusion. At the end of the day you're still writing code for a web browser - not some kind of proprietary angular engine. – Simon_Weaver Feb 20 '19 at 05:07
  • 3
    @Simon_Weaver I respect your opinion and thanks for sharing. I am just surfacing it because it is what I used to circumvent the deprecation. I also surfaced the caveats. – AliF50 Feb 20 '19 at 15:19
  • 7
    @AliF50 "Circumventing deprectaion" is not really a thing. The real problem here is that, and I've never seen this before in my life, they deprecated it _without naming an alternative_. My answer (the accepted one above) explains my hypothesis as to why they did the (W3C deprecated it) to align with the spec. However, if you read the proposals, it looks like ::ng-deep will be replaced with a suitable alternative which means that when it becomes available, you simply update your ::ng-deep references, rather than your approach which calls for literally re-architecting the entire application. – dudewad Feb 20 '19 at 20:06
  • 1
    Most of us use scss. So just wrap the all styles of the component in it's own class and you are safe with `ViewEncapsulation.NONE`. – Hannes Schneidermayer May 02 '22 at 14:24
29

As someone stated before, if you're using a third party library it's virtually impossible to avoid the ::ng-deep once in a while.

Let's have a look to some alternatives

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

Be aware that breaking the encapsulation of the component will make the styles globally available. I can think in 2 ways to avoid collisions and CSS weirdness:

  • Wrap your component's template with a class. So, example.component.html should be like:
<section class="app-example-container">
<!-- a third party component -->
<mat-tab-group>
<mat-tab label="First"></mat-tab>
<mat-tab label="Second"></mat-tab>
</mat-tab-group>
</section>

Now, since there's no Encapsulation you can modify the third party component by targeting their classes. That said, example.component.scss should be like:

.app-example-container {
/* All the CSS code goes here */
.mat-tab-group .mat-tab-label {color: red;}
}
  • or use the component's tag name as wrapper. For example:
app-example {
/* All the CSS code goes here */
.mat-tab-group .mat-tab-label {color: red;}
}
  1. Use Global styles

As simple as adding a new CSS file to your styles array of your angular.json configuration file. Be aware that this will eventually become harder and harder to maintain. Personally, I'd avoid this option or use it as a last resort :)

  1. Use a directive

I will agree that it's a bit painful because you can't include Styles in a directive (in the same way you can do it in a Component), but it can be handy sometimes. You could also use a component to apply the styles the same way the Angular Material team did with the buttons

  1. :host ::ng-deep

You already know about this one, just wanted to make clear that using it together with the host selector is the Angular recommended way to avoid potential styles collisions.

A self note to the future: https://angular.io/guide/component-styles
That should be the first place to look at for official alternatives/ways-to-go

  1. Encourage library authors to use CSS variables that you could customize from the parent component or shadow parts (when possible). The Ionic Team did a great job at these. For a more detailed explanation you can check here

Edit 1. As @beppe9000 mentioned in a comment, ::ng-deep is an Angular thing. Even if the Angular team remove this feature tomorrow, your already deployed app will continue working. The confusion arose because of the old /deep/ modifier.

WSD
  • 3,243
  • 26
  • 38
  • `But what are you going to do about your previous projects when the ::ng-deep became no longer supported by browsers?` Isn't that compiled/polyfilled by angular cli so there is no browser involvement? Great answer btw. – beppe9000 Dec 11 '20 at 22:37
  • yeah makes sense to avoid re-deploy – beppe9000 Dec 14 '20 at 11:03
3

This is not a general replacement for ::ng-deep, but for the use case described by the question author:

In the special case where you want to style the element inserted by a router-outlet, there is an elegant solution using the adjacent neighbor selector in CSS:

router-outlet+* {
  /* styling here... */
}

This will apply to all elements which are direct neighbors of a router-outlet.

Further reading:
https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator
https://angular.io/guide/router#router-outlet

mrm1st3r
  • 39
  • 5
  • 1
    I would not recommend using this selector. This seems like you're opening up a literal nightmare of collisions especially when your application grows. On top of that, the * selector is literally the single slowest selector in CSS existence. – dudewad Feb 20 '19 at 20:07
  • @dudewad while the * selector is the slowest selector, it's only being applied to literally the next sibling (+), not the entire chain/tree, so it should make only a nominal difference. – Erik Philips Nov 09 '20 at 05:29
  • @ErikPhilips CSS selectors are parsed right-to-left, so this is actually a worst-case scenario. – dudewad Nov 09 '20 at 21:43
  • @dudewad I think we're missing something. `*` is worst case scenario followed closely by `element *` but `element + *` is no where near the first two. – Erik Philips Nov 10 '20 at 04:58
  • I dont know... I haven't tested it, this is just based off what I know about how CSS parsers do their thing. – dudewad Nov 12 '20 at 23:58
0

If you want to style some 3rd party component which is used across the site in several places, but you need specific CSS look in one place without affecting other occurences after visiting THIS page - you can use [ngStyle]. I understand it's not suitable for all situations, but in my cases it is a way to go (I need to have ViewEncapsulation.None, so I cannot isolate styles in the separate CSS file).

Alexander
  • 1,152
  • 1
  • 16
  • 18
-14

You can use "/deep/". It's ::ng-deep alternative.

:host /deep/ h3 {
  font-style: italic;
}
Ishita Ray
  • 672
  • 3
  • 8