Skip to content

Shadow DOM Styling

Welcome to our comprehensive guide on mastering JavaScript Shadow DOM styling. In this tutorial, we'll delve into the intricacies of Shadow DOM styling, providing you with the knowledge and skills to leverage this powerful feature effectively in your web development projects.

Understanding the Shadow DOM

Before diving into styling, let's ensure we have a solid understanding of what the Shadow DOM is. The Shadow DOM, or Shadow Document Object Model, is a web standard that allows encapsulation of DOM and CSS in a separate, isolated scope. It enables developers to create components with scoped styling and functionality, preventing style leakage and conflicts between different parts of the web page. Also read our previous page in this book, Shadow DOM.

Creating Shadow DOM

To start working with Shadow DOM, we first need to create it. Let's take a look at how to do this using JavaScript:


html
<body>
  <div id="my-element"></div>
    <script>
      // Creating Shadow DOM
      const shadowRoot = document.getElementById('my-element').attachShadow({ mode: 'open' });
    
      // Styling Shadow DOM
      shadowRoot.innerHTML = `
        <p>A simple shadow root content.</p>
      `;
    </script>
</body>

In this example, we create a <div> element with the ID "my-element" and attach a Shadow Root to it using the attachShadow() method. We specify the mode as 'open', which allows us to access and modify the Shadow DOM from JavaScript.

Styling Shadow DOM

WARNING

Minimize the mixing of styles between the Shadow DOM and Light DOM, as it can introduce complexity and potential conflicts.

Once we have our Shadow DOM set up, we can start styling it to achieve the desired appearance. Shadow DOM styling follows a similar syntax to regular CSS, but with a few key differences:

Scoped Styles

Styles defined within the Shadow DOM are scoped to that particular Shadow Tree, meaning they won't affect the styles outside of it. Let's see how we can apply styles to elements within the Shadow DOM:


html
<div id="my-element">
  <!-- Shadow DOM content -->
</div>

<script>
  const shadowRoot = document.getElementById('my-element').attachShadow({ mode: 'open' });

  shadowRoot.innerHTML = `
    <style>
      /* Scoped styles */
      :host {
        display: block;
        border: 2px solid #333;
        padding: 10px;
      }

      p {
        color: blue;
      }
    </style>

    <p>This paragraph is styled within the Shadow DOM.</p>
  `;
</script>

In this example, we define scoped styles within the <style> tag inside the Shadow DOM. The :host pseudo-class targets the host element (in this case, the <div> with the ID "my-element"), allowing us to apply styles directly to it.

Styling slotted content

The ::slotted() pseudo-element allows you to style content projected into a custom element via slots. This provides precise control over the appearance of slotted content while maintaining encapsulation within the Shadow DOM.


html
<body>
<script>
  class CustomButton extends HTMLElement {
    constructor() {
      super();
      const shadowRoot = this.attachShadow({ mode: 'open' });

      shadowRoot.innerHTML = `
        <style>
          :host {
            display: inline-block;
            padding: 10px 20px;
            background-color: #007bff;
            color: #fff;
            border: none;
            cursor: pointer;
          }

          :host(:hover) {
            background-color: #0056b3;
          }

          button {
            font-weight: bold;
            border: none;
            background: none;
            color: inherit;
            cursor: inherit;
            padding: 0;
          }

          /* Styling slotted content */
          ::slotted(span) {
            font-style: italic;
            text-decoration: underline;
          }
        </style>

        <button>
          <slot></slot>
        </button>
      `;
    }
  }

  customElements.define('custom-button', CustomButton);
</script>

<!-- Test custom-button with slotted content -->
<custom-button id="my-button">Click <span>here</span></custom-button>
</body>

In this example, we define a custom <custom-button> element with Shadow DOM styling. We use the ::slotted() pseudo-element to target the <span> inside the slot. The styling applies only to the slotted <span>, leaving other text outside the slot unaffected.

Conclusion

In conclusion, mastering JavaScript Shadow DOM styling opens up a world of possibilities for creating reusable and encapsulated web components with scoped styling. By understanding the principles and techniques outlined in this guide, you'll be well-equipped to leverage Shadow DOM effectively in your web development projects. Happy coding!

Practice

What feature of the Shadow DOM allows developers to style content projected into custom elements?

Do you find this helpful?

Dual-run preview — compare with live Symfony routes.