Mastering the Mysteries of the Shadow DOM: An Unseen Universe in Web Development

Bablu Singh
Torq AI
Published in
5 min readJun 25, 2023

Handling styling for one website is indeed manageable, but when it comes to adapting the styling of your components to various websites, it can be quite a challenging task. However, fear not! You know what I found, I found a remarkable solution called Shadow DOM. 🎉✨

  • What is Shadow DOM?
  • Why is Shadow DOM used?
  • What are the different modes in Shadow DOM?
  • What are the benefits of Shadow DOM?
  • How can styling be used in Shadow DOM?

What is Shadow DOM?

Shadow DOM is a web standard that allows encapsulation and scoping of styles and markup within a specific component or element, isolating it from the rest of the page. It enables the creation of reusable web components with their own DOM tree, CSS styles, and JavaScript behaviour. The encapsulated elements and styles inside the shadow DOM are hidden from the main document, preventing interference from external styles and scripts.

i.e.: Let’s say I am creating a custom button component called “my-button.” By utilizing the shadow DOM, you can define the button’s HTML structure, CSS styles, and JavaScript functionality within its own encapsulated scope. The component’s styles won’t be affected by the global styles of the page, ensuring consistent appearance and behaviour. Other developers can then use your “my-button” component without worrying about conflicts with their existing CSS or JavaScript code.

export default function App() {
useEffect(() => {
const parentDiv = document.createElement("div");
const shadowRoot = parentDiv.attachShadow({ mode: "open" });
shadowRoot.innerHTML=
`<button id="withShadow">Shadow Button</button>`;

const buttonStyles = `
#withShadow {
background-color: green;
color: yellow;
border: none;
cursor: pointer;
}
`;

const style = document.createElement('style');
style.textContent = buttonStyles;
shadowRoot.appendChild(style);
document.body.appendChild(parentDiv);
}, []);

return (
<>
<button>Normal Button </button>
</>
);
}
Normal button and shadow dom enabled button
Preview of the above code snippet

In the image, you can see two buttons one is a button without shadow dom and the other is with shadow dom enabled.
I applied some style over the button inside shadow dom but it’s not impacting the styling of the overall ui.

Why is Shadow DOM used?

Basically, the answer is the same as above.
Shadow DOM is used to encapsulate the styles, structure, and behaviour of a web component, ensuring that it doesn’t interfere with or get affected by the styles and scripts of the surrounding page. It allows developers to create self-contained components with their own DOM tree, CSS styles, and JavaScript logic, making them reusable and isolated. This encapsulation enhances modularity, reduces conflicts, and improves code maintainability, resulting in more robust and scalable web applications.

What are the different modes in Shadow DOM?

There are three different modes in Shadow DOM:

  1. Open mode: This mode allows the styles and elements within the Shadow DOM to be accessed and manipulated from outside the component.
  2. Closed mode: In this mode, the Shadow DOM is completely encapsulated, preventing any direct access or manipulation from outside the component. The styles and elements remain isolated.
  3. Shadow mode: This mode is similar to closed mode, but it also hides the existence of the Shadow DOM. The styles and elements are kept hidden, providing complete encapsulation and privacy.

These modes provide flexibility in controlling the accessibility and visibility of the Shadow DOM, depending on the requirements of the web component.

What are the benefits of Shadow DOM?

There are a lot of benefits of using shadow DOM but I will mention a few which I practically experienced.

  1. Encapsulation: Shadow DOM allows us to encapsulate the styles, structure, and behaviour of a web component, preventing it from interfering with or being affected by the styles and scripts of the surrounding page.
  2. Modularity: With Shadow DOM, components become self-contained entities that can be reused across different parts of a web application, promoting modularity and code reusability.
  3. Isolation: The encapsulation provided by Shadow DOM isolates components from the rest of the page, reducing conflicts and making it easier to manage and maintain complex web applications.
  4. Style Scoping: Shadow DOM ensures that CSS styles defined within a component only apply to the elements within the component, avoiding unintended style leaks and collisions with other parts of the page.
  5. Improved Performance: Shadow DOM optimizes rendering performance by allowing the browser to make efficient updates within the encapsulated components, minimizing the need for global repaints and reflows.

How can styling be used in Shadow DOM?

I found four ways of styling shadow dom.

1. Internal Styling:

const shadowRoot = element.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
/* CSS rules specific to the Shadow DOM */
h1 {
color: red;
}
</style>
<h1>Hello, Shadow DOM!</h1>
`;

In this example, the styles are defined within the <style> tag inside the Shadow DOM. These styles will only apply to the elements within the Shadow DOM.

2. External Stylesheets:

const shadowRoot = element.attachShadow({ mode: 'open' });
const linkElem = document.createElement('link');
linkElem.setAttribute('rel', 'stylesheet');
linkElem.setAttribute('href', 'styles.css');
shadowRoot.appendChild(linkElem);
shadowRoot.innerHTML = `
<h1>Hello, Shadow DOM!</h1>
`;

Here, an external stylesheet (styles.css) is linked to the Shadow DOM using an <link> element. The styles defined in the external stylesheet will be applied to the elements within the Shadow DOM.

3. CSS Custom Properties:

const shadowRoot = element.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
/* CSS rules using custom properties */
h1 {
color: var(--text-color, red);
}
</style>
<h1>Hello, Shadow DOM!</h1>
`;

CSS custom properties can be used within the Shadow DOM to define flexible and themeable styles. In this example, the colour of the <h1> element is set using the custom property --text-color.

4. Using the CSSStyleSheet object

const shadowRoot = element.attachShadow({ mode: 'open' });

const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync(`
h1 {
color: red;
}
`);

shadowRoot.adoptedStyleSheets = [stylesheet];

shadowRoot.innerHTML = `
<h1>Hello, Shadow DOM!</h1>
`;

In this approach, an CSSStyleSheet object is created and styles are added to it using the replaceSync() method. The adoptedStyleSheets property of the Shadow Root is then used to apply the stylesheet to the Shadow DOM. The styles defined in the stylesheet will be applied to the elements within the Shadow DOM.

From the mentioned above strategies I found using the CSSStylesSheet object efficient and easy to use as well.

Dom tree after applying shadow dom
DOM tree after applying shadow dom

Conclusion

In conclusion, Shadow DOM can be utilized to encapsulate the styles and markup of components, ensuring they remain unaffected by the rest of the page. This promotes code reusability and maintainability by creating self-contained elements. The accessibility and visibility of the encapsulated content can be controlled using open, closed, or shadow modes. Furthermore, Shadow DOM enhances performance by minimizing global repaints and reflows, while also enabling precise style scoping. Overall, it offers an effective solution for managing component styling, improving code organization, and optimizing web application performance.

--

--

Bablu Singh
Torq AI
Editor for

I enjoy sharing my knowledge and experience with others on Medium.