29

I am searching a way to styling shadow DOM from the outside. For example, I would like to set the color of all text in all 'span.special' elements as RED. Including 'span.special' elements from shadow DOM. How I can do this?

Previously there were ::shadow pseudo-element and /deep/ combinator aka >>> for this purpose. So I could write something like

span.special, *::shadow span.special {
    color: red
}

But now ::shadow, /deep/ and >>> are deprecated. So, what do we have as a replacement of them?

Supersharp
  • 29,002
  • 9
  • 92
  • 134
Okavango
  • 359
  • 1
  • 3
  • 6

4 Answers4

14

I did try many methods, including those described here. Since I'm using an external Web Component lib, I don't have access to modify these components. So, the only solution that worked for me was using JS querySelector, like this:

document.querySelector("the-element.with-shadow-dom")
  .shadowRoot.querySelector(".some-selector").setAttribute("style", "color: black");

Not the best solution, not suitable for large stylings, but does work for little enhancements.

@John this was tested with Chrome 83.0.4103.116 (still going to test in Safari) and I did for Ionic (v5) ion-toast component. Here is the (almost) real code I used:

  import { toastController } from '@ionic/core';

  let toastOpts = {
    message: "Some message goes here.",
    cssClass: "toast-with-vertical-buttons",
    buttons: [
      {
        text: "Button 1",
        side: 'end'
      },
      {  
        text: "Button2",
        side: 'end'
      },
      {
        icon: "close",
        side: "start"
      }
    ]
  }
  toastController.create(toastOpts).then(async p => {
    let toast = await p.present(); // this renders ion-toast component and returns HTMLIonToastElement
    toast.shadowRoot.querySelector('div.toast-button-group-end').setAttribute("style", "flex-direction: column");
  });
Bruno Polo
  • 657
  • 5
  • 11
  • Could you please provide an example with a generic HTML5 element and clarify which browsers it worked in? – John Jun 30 '20 at 04:34
  • 1
    Seems to work though I was really only interested in styling the shadow DOM of things like `input` elements and in Waterfox 56 specifically. Still, I haven't seen anyone else post code like yours, thank you. – John Jun 30 '20 at 21:07
12

There is still no easy way to pierce through the shadow root, but here are 3 ways you can go about it. Just keep in mind that you will need to make changes inside the web component.

  1. Using variables v1 - You will need to pass the property and consume the variable inside the web component.

  2. Using variables v2 - You will need to consume the variable inside the web component.

  3. Using ::part() - You will need to add a part attribute to the element you want to style in the web component. (Note: this pseudo element is well supported but is still in experimental mode, so make sure you're aware of that before using it in production).

Run code sample below for details.

const elA = document.querySelector('custom-container-a');
const shadowRootA = elA.attachShadow({mode:'open'});
shadowRootA.innerHTML = '<style>:host([border]) {display:block;border: var(--custom-border);}</style>'+
    '<p>Shadow content A</p>'
  
  
const elB = document.querySelector('custom-container-b');
const shadowRootB = elB.attachShadow({mode:'open'});
shadowRootB.innerHTML = '<style>p {display:block;color: var(--custom-color, blue);}</style>'+
    '<p>Shadow content B</p>'


const elC = document.querySelector('custom-container-c');
const shadowRootC = elC.attachShadow({mode:'open'});
shadowRootC.innerHTML = '<p part="paragraph">Shadow content C</p>'
/* Normal way of styling */
p {
  color: orange;
}

/* Using variables version 1 */
custom-container-a {
  --custom-border: 3px solid gold;
}

/* Using variables version 2 */
custom-container-b {
  --custom-color: green;
}


/* Using ::part() */
custom-container-c::part(paragraph) {
  color: magenta;
}
<p>Light content</p>
<custom-container-a border></custom-container-a>
<custom-container-b></custom-container-b>
<custom-container-c></custom-container-c>
Gene Parcellano
  • 5,799
  • 5
  • 36
  • 43
9

You could use @import css as explained in this answer to another question on SO.

Include the rule inside the style element in the shadow tree.

 <style>
   @import url( '/css/external-styles.css' )
 </style>

Note that the >>> combinator is still part of the CSS Scoping Module Draft.

Community
  • 1
  • 1
Supersharp
  • 29,002
  • 9
  • 92
  • 134
7

Well, @import is not a solution if you are working with library web component that you can't change ...

Finally I found several ways to do it:

1) Cascading. Styles of Shadow DOM's host element affect Shadow DOM elements also. Not an option if you need to style a particular element of the Shadow DOM, not every.

2) Custom properties https://www.polymer-project.org/1.0/docs/devguide/styling If an author of the web component provided such.

3) In Polymer, the have Custom Mixins also https://www.polymer-project.org/1.0/docs/devguide/styling

4) @import, but only for not-library components

So, there are several possibilities, but all of them are limited. No powerful enough way to outside styling as ::shadow were.

Okavango
  • 359
  • 1
  • 3
  • 6