
Style Blocker: How To Prevent CSS Cascade With Shadow DOM
Matthew James Taylor23 February 2022
I was recently working on my responsive columns layout system when I encountered a problem.
I wanted to include a demo layout within an article, but I didn't want any of my normal website styles to affect the layout.
Essentially, I wanted to isolate my layout so it was completely unaware of the surrounding page CSS. This meant everything... font family, link colors, heading sizes, EVERYTHING!
How was I going to do that?
My first thought was to use an iframe, but that seemed too messy.
An iframe requires a separate page for the layout which is not good for SEO:
- Google won't index iframe content as part of the parent page.
- Google would likely index the layout as an additional out-of-context page.
I wanted to avoid both of these issues.
An iframe was not the answer.
So I did a bit of research and that lead me to the dark side... the shadow DOM.
I've looked at the shadow DOM before for other things and it always felt a bit too complicated for my needs, but for blocking styles... it seemed PERFECT!
After a bit of experimentation, I came up with the following solution.
Style-Blocker: A Custom Element To Isolate CSS
The first thing I did was to create a custom HTML element <style-blocker>
that I could use as an isolation barrier to block CSS.
I then placed my layout inside the <style-blocker>
element like this.
<style-blocker>
<!-- layout html here -->
</style-blocker>
At this point, nothing has changed. The surrounding page styles still leak into my layout. This is because even though my layout is inside the <style-blocker>
element, it's not inside its shadow DOM.
To fix this, we need to complete the following steps with javascript:
- Register the
<style-blocker>
custom element. - Create a shadow root on
<style-blocker>
. - Move all descendent elements of
<style-blocker>
into its shadow root.
I included a script element on the page below my layout, and wrote the following javascript to complete the above steps:
<script>
class StyleBlocker extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
while (this.childNodes.length > 0) {
this.shadowRoot.appendChild(this.childNodes[0]);
}
}
}
window.customElements.define('style-blocker', StyleBlocker);
</script>
Note: If you include whitespace between the opening <style-blocker>
element and the first element inside, or likewise, between the last element inside and the closing </style-blocker>
then the script will copy this whitespace across into the shadow DOM too. To prevent this, just remove leading and trailing whitespace.
Success!
My layout is now completely isolated from the surrounding page styles! =)
The best thing about this approach is it's SEO-friendly.
Google will index the complete page including the layout because everything is included in the original HTML source of the page.
Even if Google decides to execute javascript on the page, it will still know about the layout inside the shadow DOM and so it should still be indexed.
I was very happy with this solution.
However.
I had another issue.
My layout requires some styles for it to work, so how do I pass the specific styles I need into the shadow DOM?
How To Pass Styles Into A Shadow DOM
To add styles to a shadow DOM, insert a link element that points to an external stylesheet file, here are the steps:
- Create a
<link>
element. - Set the
href
attribute to the CSS filename. - Set the
rel
attribute to 'stylesheet' so the browser knows it's for CSS rules. - Append the
<link>
element to the shadow DOM.
Rather than hardcode the stylesheet filename in my javascript, I decided to add a css-files
attribute to my <style-blocker>
element so I can easily pass in optional CSS files.
To make things super-flexible I allow for a comma-separated list of files just in case I need to pass in more than one.
Here's the HTML showing the extra attribute on style-blocker:
<style-blocker css-files="/css/layout.css">
<!-- layout html here -->
</style-blocker>
Here's the completed javascript including CSS injection:
<script>
class StyleBlocker extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
while (this.childNodes.length > 0) {
this.shadowRoot.appendChild(this.childNodes[0]);
}
const cssFiles = this.getAttribute('css-files');
if (cssFiles) {
const arrCssFiles = cssFiles.split(',');
for(let i = 0; i < arrCssFiles.length; i++){
let link = document.createElement('link');
link.setAttribute('rel', 'stylesheet');
link.setAttribute('href', arrCssFiles[i]);
this.shadowRoot.appendChild(link);
}
}
}
}
window.customElements.define('style-blocker', StyleBlocker);
</script>
Now I can easily isolate parts of my page from the surrounding page styles and inject alternate stylesheets.
A perfect solution that's easy to do!
Shadow DOM vs Iframe
There are some important differences between shadow DOMs and iframes that I want to point out.
Iframes:
- Completely separate indexable page.
- Content not indexed as part of the parent page.
- Iframe width is equal to the viewport width, this means any breakpoints you have set up will be relative to the width of the iframe, not the parent page.
Shadow DOM:
- Part of the main page.
- Can remain indexable by Google when included in the original page source.
- Breakpoints are relative to the width of the parent page, not the shadow DOM element.
Summary
The shadow DOM makes it easy to block surrounding page styles from cascading into specific elements. Injecting styles into a shadow DOM is easy too. The methods I've described above are also SEO friendly so there is no danger of Google not indexing important parts of your page.
Want a live demo of style-blocker? Check out my article: Holy grail 3-column responsive layout, the demo layout near the top of the page is in a shadow DOM.
- Use your browser inspect tool to navigate around the shadow root.
- Turn off javascript and the layout will still be visible, just a non-isolated version.
Overall, I'm impressed with what the shadow DOM can do so I will be experimenting with it further.
Next up:
How to add CSS to HTML ›
Responsive Font Size ›
Web Design Articles



Equal-Height Columns (CSS Grid, Flexbox, Floated Containers, & Table Methods)
Web design
How to add CSS to HTML (With Link, Embed, Import, and Inline styles)
Web design



Responsive Columns: Build Amazing Layouts With Custom HTML Tags
Web design




