简体   繁体   中英

Shadow DOM styling from the outside

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?

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.

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.

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 enchancements.

@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");
  });

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() NOTE: The specs for this hasn't been finalized. So it might not be a good idea to use in production. */ 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM