Search code examples
web-componentshadow-domcustom-elementgoogle-web-componentnative-web-component

Global CSS effect on Shadow element. Why?


Both CSS (one in light dom and one in shadown dom) effect on tag.
Name and Phone are effected by global CSS at and Shadow Scope CSS too!!!
WHY ????? I dont want it.
I expected they are just effeted by Sahdow scope CSS which is in template scope.
I wish I have some ideas from you.

https://plnkr.co/edit/Asr1S1UFvhmhtZeWm5k8

//CREATE CUSTOM ELEMENT
var MyContactProtype = Object.create(HTMLElement.prototype);
MyContactProtype.createdCallback = function() {
  //retrieve template
  var tpl = document.querySelector('#contact-form-tpl');
  var tpl_ct = document.importNode(tpl.content, true);

  //this <=> my-contact element -> create shadow
  var shadowRoot = this.createShadowRoot();

  //show template in shadow DOM
  shadowRoot.appendChild(tpl_ct);
};

//REGISTER CUSTOM ELEMENT
document.registerElement("my-contact", {
  prototype: MyContactProtype
});
span {/* global CSS*/
  text-decoration: line-through
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/document-register-element/1.11.0/document-register-element.js"></script>
<span>HELLO</span>
<!--line through by css at LINE 6: OK-->
<my-contact>
  <h1 class="header-contact-form">Contact X</h1>
  <span class="name">this is my name</span>
  <span class="phone">this is my phone</span>
</my-contact>


<template id="contact-form-tpl">
  <style>
    span {/* shadow scope CSS*/
      text-decoration: underline
    }
  </style>
  <fieldset>
    <legend>
      <content select="h1.header-contact-form"></content>
      <!--
      Name and Phone are effected by CSS at line 6 and 21 too!!!
      WHY ????? I dont want it.
      I expected they are just effeted by CSS line 21 which is in template scope.
      -->
      <div>
        Name: <span><content select="span.name"></content></span>
      </div>
      <div>
        Phone: <content select="span.phone"><span></span></content>
      </div>
      <span>TEST</span><!-- only apply css at line 21: OK-->
    </legend>
  </fieldset>
</template>


Solution

  • It's the normal behavior of CSS styles. The elements inserted in the Shadow DOM with <content> (in your example: <span class=name>...</span>) are actually part of the normal DOM, and therefore are affected by the global CSS styles.

    If you don't dont want it, you should try another technique, like copying the light DOM elements into the Shadow DOM instead of using <content>.

    Also, you should use Custom Elements v1 (and customElements.define()) and Shadow DOM v1 (and <slot>) instead of Custom Elements v0 (registerElement()) and Shadow DOM v0 (<content>) which are deprecated.

    With Shadow DOM v1, you can use ::slotted() inside the Shadow DOM to select and style insterted elements.

    You can then overload the CSS rule in the Shadow DOM <style> with the !important modifier:

    line 21:

    <style>
        ::slotted( span ) {
          text-decoration: underline!important
        }
    </style>
    

    Below is the complete snippet:

    //CREATE CUSTOM ELEMENT
    class MyContact extends HTMLElement {
      constructor() {
         super()
        //retrieve template
        var tpl = document.querySelector('#contact-form-tpl');
        var tpl_ct = document.importNode(tpl.content, true);
    
        //this <=> my-contact element -> create shadow
        var shadowRoot = this.attachShadow( {mode:'open'}) //createShadowRoot();
    
        //show template in shadow DOM
        shadowRoot.appendChild(tpl_ct);
      }
    }
    
    //REGISTER CUSTOM ELEMENT
    customElements.define("my-contact", MyContact);
    span {
      text-decoration: line-through
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/document-register-element/1.11.0/document-register-element.js"></script>
    <span>HELLO</span>
    <!--line through by css at LINE 6: OK-->
    <my-contact>
      <h1 slot=header>Contact X</h1>
      <span slot=name>this is my name</span>
      <span slot=phone>this is my phone</span>
    </my-contact>
    
    
    <template id="contact-form-tpl">
      <style>   
        span ::slotted( span ), 
        span { 
            text-decoration:underline!important  
        }
      </style>
      <fieldset>
        <legend>
          <slot name=header></slot>
          <div>
            Name: <span><slot name="name"></slot></span>
          </div>
          <div>
            Phone: <slot name="phone"><span></span></slot>
          </div>
          <span>TEST</span><!-- only apply css at line 21: OK-->
        </legend>
      </fieldset>
    </template>