What is the Correct Way to Style a 3rd Party Component Without using ::ng-deep, /deep/, or >>> Combinators?
Asked Answered
B

5

9

I've been looking for a definitive answer to this question for a long time. Is there a reliable and recommended alternative strategy to do this yet? Incorrect answers to this question include:

Just favor ::ng-deep for now

and

if component author didn't integrate styling into their API, you're out of luck

According to the docs, all 3 of these combinators are deprecated, so what's the "right way" of going about this?

Edit:

The answers suggesting using a strategy of global styling literally answer the question and are appreciated. However, Angular is a component-based framwork, and view encapsulation is one of the core boons that make it a valuable tool. To be fair to those providing an answer, this was not specified in the question. Still, the general use-case and desired behavior is to keep view encapsulation, so such a dramatic change in workflow is not a reasonable solution for the majority of cases, i.e. a "right way".

Bedcover answered 5/1, 2019 at 19:12 Comment(1)
There is no right way to break encapsulation.Antre
S
5

You cannot place the style globally unless you want to change all the instances. Currently, there is no correct way to achieve what you ask.

The angular documentation states that /deep/ and >>> are deprecated. ng-deep, is deprecated too, but the documentation does not provide a way to achieve the desired goal to fix a 3rd party component instance in angular. The common practice inferred by the documentation is to use the deprecated ng-deep operator while the angular team figures out what to do.

Clearly, the user of ng-deep is not clean, but there is no alternative at the moment.

Sirmons answered 5/1, 2019 at 19:41 Comment(2)
When using global styles can't you just do something like: .parent-component .third-party { color: red; } This should allow you to restrict the styles to a parent component if need be.Formalin
You can do it if you can find appropriate constraints that can be used in that particular case. However, besides trying to do the bad thing, the common usage here is to fix 3-party styles that aren't designed (or simply cannot) to be changed by you. Your approach might work in certain cases, but may fail in others.Sirmons
L
1

Add them to your global styles.

Lulita answered 5/1, 2019 at 19:31 Comment(6)
The problems I see with this approach are: potential naming collisions across multiple vendors and your own styles., as well as having to know precisely what selectors are identifying which DOM elements to avoid triggering inadvertent changes. Bascially, the problems that view encapsulation is trying to solve. View encapsulation is a useful tool, we just need a future-proof way to break it when needed I believe.Bedcover
Not really. I mean you're trying to style a 3rd party component. It'll most likely have a very specific tag name. Look at Material for example. That would obviously apply it to all the instances of the component no matter where it appears in your app, but you can remedy it by making an even more specific selector: my-component third-party-component{ //styles }Lulita
That way it'll only apply the styles to the third party component that appears within your desired component. Almost as good as encapsulated styles while there isn't anything better.Lulita
With this approach, how do you penetrate view-encapsulated components to style a specific tag inside of them? Adding mat-component > div.some-class to your global stylesheet won't work unless you also deactivate view encapsulation globally, right?Bedcover
It'll work if you add it to your styles.scss/styles.css file. That's where all the global styles go.Lulita
@KeenanDiggs, No you should not have to deactivate view encapsulation for it to work.Formalin
I
1

I'd agree with Christian that the best way is to add them to your global styles, since then you know they are global styles.

A similar method is to use encapsulation: ViewEncapsulation.None as a meta-tag on your Component. All this is really doing, though, is lazy-loading some global styles, which means once that component is created, those styles are applied globally across the site, even after the component is "destroyed".

I ran into a similar issue recently and ended up using ViewEncapsulation.None and then just writing very specific CSS selectors (e.g. #idTag > .third-party-class-1 > .third-party-class-2 > .third-party-class-4). This is brittle, as any shift of elements by the 3rd party library throws off your styling.

There is an open issue on angular that discusses this issue with ViewEncapsulation.None, so hopefully there is a resolution on it soon.

Impervious answered 5/1, 2019 at 20:50 Comment(1)
Specifically, ViewEncapsulation.None adds the styles to the document head, so yes it's basically global styles.Bedcover
S
0

ng-deep has been deprecated for years, but is still the go-to solution I've seen in many production app. It's deprecated but that doesn't mean it will be removed - especially in this particular case

As such we plan to drop support in Angular (for all 3 of /deep/, >>> and ::ng-deep). Until then ::ng-deep should be preferred for a broader compatibility with the tools

Note: be sure to include the :host selector before ::ng-deep. If the ::ng-deep combinator is used without the :host pseudo-class selector, the style can bleed into other components

https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep

Stifling answered 17/7, 2020 at 8:0 Comment(0)
C
0

After I initialize the third party component, I do

const div document.querySelector('#div .something') as HTMLElement;

or something similar to get it. Then I manually edit the CSS like,

if (div) {
   div.style.color = "#000000"
}

If they used css parts

<template id="custom-element">
   <div part="tab">Tab C</div>
</template>

You can do something like

custom-element::part(tab) {
  /* Styles */
}

straight in your .css file for the component.

If they have it in a shadow dom and do not use parts, you do something like

const hiddenDiv = parentDiv.shadowRoot.getElementById('hiddenDiv')
if (hiddenDiv) {
   hiddenDiv.style.color = "#000000"
}

Sometimes I need the css to updated dynamically upon resize of the component, so I use an ResizeObserver or MutationObserver.

I don't know if this is the proper way to do it, someone mentioned there's no proper way to break encapsulation and they're probably right, but it works, and I do think it is a better solution than the other answers in this thread.

I structure my code so that it's more readable and try to encapsulate any css editing in at least its own function so as to minimize the effect of not having all of my CSS in .css files only.

Calx answered 13/9, 2024 at 17:58 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.