The Shadow DOM is a web standard that allows developers to encapsulate a piece of the DOM and CSS in a way that isolates it from the rest of the document. This encapsulation provides several benefits, such as avoiding CSS conflicts and making reusable web components more manageable. You can read more about shadow DOM in MDN docs.
Shadow DOM allows you to create a subtree of DOM elements that is isolated from the main document’s DOM. This means that styles and scripts within the Shadow DOM do not affect the rest of the document, and vice versa.
The entry point of the Shadow DOM is called the Shadow Root. You can attach a Shadow Root to an element using JavaScript, and this root acts as a boundary for the encapsulated DOM.
Styles defined inside the Shadow DOM do not leak out to the main document, and styles from the main document do not affect the Shadow DOM. This isolation helps in preventing unintended side effects and conflicts.
Here’s a simple example demonstrating how to use Shadow DOM:
<!DOCTYPE html><html><head> <style> /* This style will not affect the Shadow DOM */ div { color: red; } </style></head><body><div id="host">Hello, world!</div><div id="example">This example is outside the DOM</div><script> // Select the host element const host = document.getElementById('host'); // Create a shadow root and attach it to the host const shadowRoot = host.attachShadow({ mode: 'open' }); // Add some content to the shadow root shadowRoot.innerHTML = ` <style> /* This style is scoped to the shadow DOM */ p { color: green; } </style> <p>This is inside the Shadow DOM</p> `;</script></body></html>
In this example:
The <div> element with the ID host is the host element for the Shadow DOM.
A Shadow Root is attached to this host element using host.attachShadow({ mode: ‘open’ }).
Inside the Shadow Root, a paragraph (<p>) element is styled with green text. This style is isolated within the Shadow DOM and does not affect the rest of the document.
The red text color defined in the main document does not affect the paragraph inside the Shadow DOM.
getAnnouncerNode() is as shown below in the same file:
Mode used is “open” meaning this shadow can be accessed by Browser’s Javascript, hence the reason why you see existingAnnouncer?.shadowRoot?.childNodes[0] in the if block.
createPortal is used to insert this shadowDom in the document.body.
I have not seen shadowRoot found in app-router-announcer before and this led me to find out about shadowDOM and how shadowRoot is related to this concept. shadow DOM is found to be used in banners/announcements(next.js) or error overlay (meteor.js) to avoid css breaks in other parts of the app because shadowDOM prevents the css override and can have its own scripts and styles that can be applied to the shadow tree.
You could apply this similar pattern to your npm packages to not let your package’s CSS affect the other parts of the app. This is one of many ways to prevent CSS overrides.
Feel free to reach out to me at ramu.narasinga@gmail.com if you have any questions about this article.
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.