[ionic-v4] shadow-dom

I migrate my app from v3 to v4 and I have many problems with CSS and shadow-dom.

How can I change the CSS for a child-element in shadow-root.

Example
I want to change css for class .button-inner in ion-button
here is my code for ionic v3
.long-text-button .button-inner {
font-size: 12px;
padding: 0px;
margin: 0px;
text-align: center;
white-space: normal;
}

I can’t use it in v4 because it wrap in shadow-root

I wrote an article about that here: https://www.joshmorony.com/shadow-dom-usage-in-ionic-web-components/

In short, the only way you can style elements inside of a Shadow DOM is if they are styled using CSS4 variables that you can override.

Thank for your reply and your blog is very helpful.
I found my solution to this problem

I create a directive and write my custom CSS to shadowRoot.

import { Directive, ElementRef, Input, OnChanges } from '@angular/core';
@Directive({
  selector: '[appShadowCss]'
})
export class ShadowCssDirective implements OnChanges {
  @Input() shadowCustomCss: string;

  ngOnChanges(): void {
    const shadow = this.el.nativeElement.shadowRoot || this.el.nativeElement.attachShadow({ mode: 'open' });
    if (shadow) {
      let innerHTML = '';
      innerHTML += '<style>';
      innerHTML += this.shadowCustomCss;
      innerHTML += '</style>';
      shadow.innerHTML += innerHTML;
    }
  }
  constructor(private el: ElementRef) {

  }

}


<ion-button expand="block" fill="outline"  appShadowCss shadowCustomCss='.button-inner{white-space:normal;}'>
             Edit your phone number.
</ion-button>

I know It is a very bad idea to do this in every button.
Do you know how to disable shadow root in ionic?

3 Likes

Well, this is a bit annoying. That is not the only way to style it. You can inject your own styles by traversing the tree using shadowRoot property but it’s ugly and requires a code change to just change a border or something simple like the width of a tab. I don’t think shadow DOM will stop people from injecting their own CSS, it’s just made it more difficult

I agree that it won’t stop people, but I can see the benefits in terms of long term support for the components. Seems to me to pretty much be a trade off of flexibility for stability. I think a lot of people will inject their own CSS which would defeat the overall purpose/goal from Ionic’s view, but I think most people will probably end up conforming to the CSS variable approach - especially as they become better documented and more variables are added.

3 Likes

I have a huge problem with a segment button that has a button inside a shadow root. I need the default value (6px) for this, but it is a first element and assumes the value applied as shown below. Can someone help me?

I think in theory you should then have all CSS properties as a variable because each has its own application and use cases. For me it is really a problem if the variable is not there then.

For me there should be an option like ::ng-deep, /deep/ to overwrite such things anyway, but unfortunately there is not. Only via javascript which is really rubbish.

2 Likes

@danielmm1997 I agree with you. In theory not using /deep/ should be good, but in practice I don’t think that’s good.

From a commentary I saw (that I agree):

Custom properties — These are not suited or intended for wholesale styling of shadow descendants, as CSS WG members have said. They are far too unwieldy when exposing lots of properties, and they put an onus on the component author to anticipate all possible use-cases in advance. If the component you’re using doesn’t expose a custom property to do what you want, you’re out of luck.

Source: While I appreciate the decision to deprecate /deep/ has put the Angular team in a difficult… | by Jon Rimmer | Medium (it’s about angular css, but based on shadow dom / native encapsulation)

In an ideal world, every library would provide every expected css variable so as to make the use of hacks such as /deep/ unecessary. Unfortunately, they are people with limited resources and time, and it’s expected that their components will have css properties that the consumers can’t override through css because of the shadow DOM. Such problems would be greatly mitigated with /deep/ .

I don’t consider shadow dom production ready while there isn’t a way to access inner components in shadow dom through css (unless I’m not using 3rd party components, which is not the case when using ionic components), because of the reasons above.

The intent of the ionic team for using it should be good, but I don’t think that the result is good, at least for now, and without a way to disable it (to use the ionic components without shadow dom).

I need your help. I wanted to do the same for nested shadow-dom. querySelector is not working on the shadow-dom. What should i do, i to want apply style to a shadow-dom inside a shadow-dom.

Kindly help.43%20PM
I wanted to change the top value (top:44px) of ion-backdrop inside ion-menu

1 Like

I agree with you.
In my opinion there are too few variables that you can use and the impossibility to modify the style of the components makes them practically unusable.
So I’have to use standard html tags instead of Ionic tags, because they are not customizable.

cld

1 Like

Hi changing styles is veryyyyy simple :slight_smile: even if there is #SHADOW DOM

Solution Source Link

HAHAHHA I found solution in my own blog after an year

If you want to change background color of for example, just add a class to it

<ion-content [fullscreen]="true" class="custom-ion-content">

Now in global.scss file add following style:

.custom-ion-content{

    --background:none;

}

But you need to check the documentation like for ion-content about properties we can change.

Did you find a solution i have the exact same problem

I used your solution and made it simpler to apply to specific elements:

Base directive

import { Directive, ElementRef, Input, OnChanges, OnInit} from "@angular/core";

/**
 * Applies css to the element's Shadow DOM subelements.
 * Usage: <ion-button appShadowCss shadowCustomCss='.button-inner{white-space:normal;}'>
 *
 * Important notice: Only use shadow css when it is absolutely necessary as it potentially
 * breaks compatibility with future ionic versions. Always check if you can use exposed element variables in
 * variables.scss instead or if you can apply css to non Shadow elements (eg., directly to ion-radio in app.scss).
 */
@Directive({
  selector: "[appShadowCss]"
})
export class ShadowCssDirective implements OnChanges, OnInit {
  @Input() shadowCustomCss = "";

  constructor(private el: ElementRef) {
  }

  ngOnInit(): void {
    const shadow = this.el.nativeElement.shadowRoot || this.el.nativeElement.attachShadow({ mode: "open" });
    if (shadow) {
      let innerHTML = "";
      innerHTML += "<style>";
      innerHTML += this.shadowCustomCss;
      innerHTML += "</style>";
      shadow.innerHTML += innerHTML;
    }
  }

  ngOnChanges(): void {
    this.ngOnInit();
  }
}

Subdirectives

import { Directive, Input } from "@angular/core";
import { ShadowCssDirective } from "./shadow-css.directive";

/**
 * Usage: <ion-radio appRadioCss></ion-radio>
 */
@Directive({
  selector: "[appRadioCss]"
})
export class RadioCssDirective extends ShadowCssDirective {
  @Input() shadowCustomCss = `
  .radio-inner {
    --border-width: 4px;
  }
  :host(.radio-checked) div.radio-icon {
    border-color: var(--color);
  }
  `;
}