ReviseAlgo Logo

DOM Manipulation

Shadow DOM Basics

Master CSS encapsulation and Shadow DOM. Learn how to create shadow roots, isolate styles, and build reusable web components.

Last Updated: July 15, 2026 10 min read

1. Introduction

In standard HTML documents, CSS classes and scripts share a single global scope. The Shadow DOM is a Web Component standard that provides encapsulation, allowing you to attach an isolated DOM subtree (a shadow tree) to an element, preventing styles and scripts from leaking out.

2. Why It Matters

Without encapsulation, styles defined in third-party widgets can conflict with your application's CSS rules. The Shadow DOM keeps styles isolated, ensuring that components (like video player widgets or comment sections) render consistently regardless of where they are used.

3. Real-World Analogy

Think of a Submarine Compartment:

  • Standard DOM (Open Deck Room): If water leaks into one section of the room, it floods the entire deck. Similarly, global CSS rules apply to all elements on the page, which can break layouts.
  • Shadow DOM (Watertight Hatch Compartment): A self-contained room with its own ventilation and lighting. Even if a flood occurs outside, the compartment remains dry. Styles and scripts defined inside a shadow root are isolated, keeping the component safe from outside CSS leaks.

4. Shadow DOM Architecture

The Shadow DOM uses several concepts:
Shadow Host: The regular DOM node that the shadow tree is attached to.
Shadow Root: The root node of the shadow tree.
Shadow Boundary: The boundary where the shadow DOM ends and the regular DOM begins. Styles do not cross this boundary.

5. Open vs Closed Mode

  • mode: 'open': Allows page scripts in the light DOM to access the shadow tree using the host.shadowRoot property. This is the recommended mode for most Web Components.
  • mode: 'closed': Prevents page scripts from accessing the shadow tree. host.shadowRoot returns null. This mode is rarely used because it is difficult to test and debug, and does not provide true security since scripts can override the constructor.

6. Practical Example

This script demonstrates creating a reusable custom element (Web Component) that uses the Shadow DOM to encapsulate its styles:

7. Common Mistakes

  • Trying to query shadow nodes using document.querySelector: Light DOM query calls (like document.querySelector) cannot see past the shadow boundary, returning null. To query nodes inside a shadow tree, call query methods on the shadow root object itself (e.g. host.shadowRoot.querySelector).
  • Expecting all elements to support attaching a shadow root: Browsers restrict which elements can host a shadow root due to security and layout constraints. Attaching a shadow root to tags like <img>, <input>, or <button> throws a DOMException.

8. Quick Quiz

Q1: What does document.querySelector('.badge') return if the element is located inside an open shadow root?

A) The matching element inside the shadow tree

B) null

Answer: B — Global query selectors cannot traverse the shadow boundary. You must call querySelector directly on the shadow root object instead.

9. Scenario-Based Challenge

The Encapsulated Profile Card Widget:

You are building a reusable Profile Card widget. It must display a user avatar, username, and description. The styles inside the widget must be completely isolated so they don't affect other elements on the host page. Write a Web Component using the Shadow DOM to implement the widget.

10. Debugging Exercise

Explain why this test assertion fails to find the button:

// Component registration
class CustomButton extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.innerHTML = `<button id="inner">Click Me</button>`;
  }
}
customElements.define('custom-btn', CustomButton);
// Test file
const btn = document.createElement('custom-btn');
document.body.appendChild(btn);

// Verify element render const testNode = document.getElementById('inner'); console.log(testNode); // logs null! Why?

View Solution

Diagnosis: The button element is located inside the shadow DOM of the custom element. Global DOM search methods (like document.getElementById) cannot traverse the shadow boundary.

Fix: Access the button by querying the custom element's shadowRoot object:

const testNode = btn.shadowRoot.getElementById('inner');
console.log(testNode); // Returns the button element successfully!

11. Interview Questions

🟢 Q1: Explain style encapsulation in the Shadow DOM and how CSS rules are isolated.

Answer: The Shadow DOM creates a shadow boundary that isolates CSS styles:
CSS Encapsulation: CSS rules declared inside the shadow tree do not leak out to affect elements on the host page. Symmetrically, styles defined on the host page do not leak into the shadow tree.
Theme support: While styles are encapsulated, you can expose styling hooks using CSS custom properties (variables) or CSS shadow parts (::part()) to allow parent pages to theme your component.

12. Production Considerations

  • Inherited Styles: While the Shadow DOM encapsulates styles, some CSS properties (like color, font-family, and line-height) are still inherited from the parent page. You can override inherited styles in your shadow root stylesheet to ensure your component renders consistently.