How can i style a shadow DOM in Ionic 4

This article can help. It covers everything from styling and customizing Ionic 4 components, to building custom web components with Stencil and then use them in your Ionic 4 projects (or any other framework, as web components are framework agnostic!).

Thank you for the Good Tutorial. But i really hate this approach (CSS4 Variables in Web Components). For me there should be an option to disable the Shadow Stuff for Web Components.

The Problem ist that if the developers of the Web Component forget to add a CSS4 Variable (or if there are new CSS Stuff because of a new Release) to the Web Component, then the only way to change a Style is with Javascript, WTF?!

4 Likes

ive tried but got an error message ‘cannot read property ‘shadowRoot’ of null’, can you help me with that ? sorry if im asking a dumb question.

Hey,

I found this sweet NPM Package which will help you do just that.
And this will work even when the CSS Variables are not pre-created.

It is super simple to use as well.
1: Install the package.
2: Import it in your TS file
import { injectStyles } from 'shadow-dom-inject-styles';

3: Add this kind of code in your Constructor or anywhere in the TS.

// Menu Styling in Shadow Root
    setTimeout(() => {
      // Select your Shadow Root
      let toolbar = document.querySelector('ion-app ion-menu') as HTMLElement;
      console.log(toolbar);

      // define style tag text
      let styles = `.menu-inner {
        background: transparent !important;
      }`;
      injectStyles(toolbar, '.menu-inner', styles);
    }, 200);

What this does is, it adds a style tag inside the selected Shadow root with your CSS String.

I had to put it inside a setTimeout because I was using it in the constructor of my app.component.ts, and by the time the code was executing, the Shadow Roots were not created yet.

But yea, this works !

1 Like

Thanks @abhayamin. This works somehow, even it’s super hacky and it causes a small UI glitch due to the timeout.

I had to fix a padding on item-item, as it has a fixed padding of 8px to the right wich I couldn’t get rid of. And I need a ion-item there as it’s part of a sliding list… gna! :slight_smile:

I overcame the glitch by keeping the element hidden till I could apply my style inside the Shadow Dom. Haven’t found any other way of doing it yet. This has become a regular piece of code for me. No other choice till the Ionic team gives more Variables to support styling Shadow Dom’s.

You do have another choice: Upgrade to Ionic 5 and use CSS shadow parts https://ionicframework.com/blog/customize-your-ionic-framework-app-with-css-shadow-parts/

2 Likes

That’s the issue with the new Shadow Parts: you can only style elements that are exposed to the outside. In this case, the class with the padding is not exposed and thus we can’t style it / remove it.

@abhayamin I change my whole page and all of its elements to “no padding” and then added the padding to every element accordingly. Also not perfect, but better than a second “rebuild” of the page. Has also some quirks, but it’s better than nothing.

This piece of code is for changing the parts that are not exposed by Ionic. Some cases, for a few designs you need that extra control.

That’s true if the thing you’re trying to style is not accessible through custom CSS properties or shadow parts, then you’re stuck. However, it seems like most things are accessible through one of these methods.

So neither --inner-padding-end nor --padding-end solved your problem? https://ionicframework.com/docs/api/item#css-custom-properties

Not sure what you were referring to when you said “this piece of code”. You seemed to indicate that your only option to style shadow DOM elements was to use that hacky shadow-dom-inject-styles package. What I’m saying is that many parts of Ionic components inside the shadow DOM can be accessed either through custom CSS properties or CSS shadow parts. You haven’t specified a specific shadow DOM element that you are trying to access though, so it’s unclear whether or not your case requires the hacky solution.

@wesleygoldsborough I am using Ionic 5 and I have a ion-button and I want to target the span with the class button-inner which is within the shadow part native of the ion-button. How can I target button-inner class to style it?

@noopurphalak You can’t. But perhaps whatever you are trying to accomplish can be achieved through custom css properties or some other way. Feel free to elaborate on what you’re trying to do.

@wesleygoldsborough Thanks for your reply. In my case, I was able to resolve the issue with targetting the shadow part itself. I wanted to acheive something like this:

progress-green-button

As you can see from the above image, the green button has a very minimum margin between the icon and the button itself. What I was actually getting was this:

green-button

I was eventually able to fix this by targetting the native part in the ion-button and setting padding:0; and margin:0; and incresing the size of the icon to that of the button.

Angular directive for cleaner development based on “shadow-dom-inject-styles” npm package

import { Directive, ElementRef, Input, OnChanges, SimpleChanges } from '@angular/core';
import { injectStyles } from 'shadow-dom-inject-styles';

@Directive({
  selector: '[shadowDomInject]'
})
export class ShadowDomInjectDirective implements OnChanges {
  @Input() shadowDomInject: string;

  constructor(private el: ElementRef) {
    console.log('ShadowDomInjectDirective constructor', el);
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes.shadowDomInject) {
      const value: string = changes.shadowDomInject.currentValue;
      const clean = value.replace(new RegExp(`
      `, 'ig'), '').replace(new RegExp(' ', 'ig'), '');
      const split: string[] = clean.split('{');
      injectStyles(this.el.nativeElement, split[0], changes.shadowDomInject.currentValue);
    }
  }

}