Search code examples
htmlcssweb-component

Why are web components light DOM style "leaking" as text to the DOM in certain cases while styling still functions?


While learning web components, I came across a peculiar case of CSS style "leaking" to the DOM apparently as a text node while it seems to work still. See in the provided image as pointed by the yellow arrow on how the CSS text shows up in the DOM as a text node. This can be tested by altering the CSS selector between * (leaks) and div (does not leak).

How could this be explained? See code and image and if you want to try out quickly, there's a pen at https://codepen.io/veksi/pen/WNvMOYg where you can switch the selector (tested with Firefox and Chrome).

<this-test>
  <div>
    <p>Some test text in div.</p>
    <p>Some more some text in div.</p>
    <p>And even more some text in div.</p>
  </div>

  <section>
    <p>Some test text in section.</p>
    <p>Some more some text in section.</p>
    <p>And even more some text in section.</p>
  </section>
</this-test>

const template = document.createElement('template');

template.innerHTML = `
  <style>
    /* Why does changing the following selector to
           this-test > *
       i.e. to the asterisk to match all elements, "leak" out while otherwise seem to work? */
    this-test > div {
      display: flex;
      flex-direction: column;
      color: hotpink;
    }

    p {
      background: #abcabc;
      padding: 1rem;
      border: 0;
      font-size: 1.5rem;
    }
  </style>  
`;

class ThisTest extends HTMLElement {
  constructor() {
    super();

    //Note that shadow root is not used.
    this.appendChild(template.content.cloneNode(true));       
  }  
}

window.customElements.define('this-test', ThisTest);

CSS style text leaking to the DOM as text node as indicated by a yellow arrow.


Solution

  • Not related to Custom Elements/Web Components

    All DOM elements have a display: setting

    By default <title> , <script> and <style> tags have display:none

    When you use * {display:block} (or any setting)
    the contents of those normally hidden elements are now displayed in the page

    It is a fun way to display contents of inline STYLE and SCRIPT tags:

    <title desc=" Title of this HTML document">Hello DOM World!</title>
    <script desc=" Logs text to the DEV console">console.log("Hello from SCRIPT")</script>
    <style>
      *{
        display:block;
        font-size:9px;
        color:red;
      }
      title,
      script{
        font-size:2em;
        color:blue;
      }
      *:hover:after{
        display:inline;
        content:attr(desc);
        color:green;
      }
    </style>

    The first red style block is what SO injects in the snippet