Search code examples
javascriptfirefoxweb-componentshadow-domhtml5-template

Shadow DOM v1 styling with polyfill


I'm trying to work with just ShadowDOM v1 (no Polymer or other web components) but I can't get the styling to work with a polyfill.

The special :host and ::slotted CSS selectors are not working.

I've tried with various polyfills, but TBH, I'm a bit lost. I have seen this question, but it's using custom elements and templates. My scenario is without these.

Can anyone help me make this code work in Firefox or Edge? - specifically, you should see three colors: blue, red and yellow, but only red works.

const shadowHtml =
  `<style>
    :host {
      background: yellow;
    }
    .inside {
      background: red;
      margin: 10px;
    }
    ::slotted(div[slot="content"]) {
      background: blue;
      color: white;
    }
    </style>
    <div>
      <p>i am yellow</p>
      <div class=inside>i am red</div>
      <slot name="content"></slot>
    </div>`;
  

const root = document.querySelector('#root');
const shadow = root.attachShadow({ mode: 'open' });
shadow.innerHTML = shadowHtml;
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Shadow DOM v1 Test</title>
  <script src="https://rawgit.com/webcomponents/webcomponentsjs/v1/webcomponents-lite.js"></script>
</head>
<body>
  <div id=root>
    <div slot="content">
      <p>I am blue</p>
    </div>
  </div>
</body>
</html>


Solution

  • webcomponents-lite.js "v1" now embeds a Shadow DOM v1 polyfill:

    • the ShadyDOM shim which provides attachShadow()
    • the ShadyCSS object which simulates :host and ::slotted().

    Note that to use it you'll have to put your template string inside a <template> element in order to call ShadyCSS.prepareTemplate()

    const makeTemplate = function (strings) {
        const template = document.createElement('template');
        template.innerHTML = strings[0];
        return template;
    }
    
    const shadowHtml =
      makeTemplate`<style>
        :host {
          background: yellow;
        }
        .inside {
          background: red;
          margin: 10px;
        }
         ::slotted(div[slot="content"]) {
          background: blue;
          color: white;
        }
        </style>
        <div>
          <p>i am yellow</p>
          <div class=inside>i am red</div>
          <slot name="content"></slot>
        </div>`
    ShadyCSS.prepareTemplate(shadowHtml, 'div');
      
    const root = document.querySelector('#root');
    const shadow = root.attachShadow({ mode: 'open' });
    shadow.innerHTML = shadowHtml.innerHTML;
    <script src="https://cdn.rawgit.com/webcomponents/shadydom/master/shadydom.min.js"></script>
    <script src="https://cdn.rawgit.com/webcomponents/shadycss/master/scoping-shim.min.js"></script>
    
    <div id=root>
      <div slot="content">
        <p>I am blue</p>
      </div>
    </div>