Scoping CSS using Shadow DOM in 2021
Scoping CSS using Shadow DOM in 2021
As a front-end developer working with a large team, managing styles for components is one of the big problems I have come across. Having a shared component style file can often result in styling conflicts, since it is very common for two developers to use the same name to name a style class.
CSS will not throw in an error for this. As a consequence, in the parts where it takes time to overcome them, I have experienced unexpected use of style. The technique I will add next will give you a solution to prevent collisions with the CSS label.
What is a Shadow DOM?
Shadow DOM is part of the DOM, which is capable of isolating JavaScript and CSS. If you have heard of iframes
, Shadow DOM is also something that has similar capabilities.
Just like in
iframes
, by default, styles within a shadow DOM won’t leak out and styles outside ofit won’t leak in.
There are ways to inherit some styles from outside of the Shadow DOM too, if required.
CSS libraries like Styled-components also solve the name collision issue by generating a random class name like .kjkgh2en3
. However,
Shadow DOM offers style encapsulation by design!
Next, let’s have a look at how to add a shadow DOM.
Can we use it in Any Project?
Although modern browsers support Web Component Specification, there are some challenges if we try to use it for existing applications.
It’s more like standardizing the Component Definitions native to browsers, where you can create new elements as we do with React, Angular &, etc.
Therefore, it’s essential to check out the feasibility of adopting Shadow DOM, depending on the component libraries you use.
Adding a Shadow DOM
There are two ways to add a shadow DOM.
- Open mode
- Closed mode
Open mode
class MyComponentOpenRoot extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.close = this.close.bind(this);
}
}
Attaching a shadow DOM with mode: 'open'
saves a reference to the shadow root on element.shadowRoot
. Therefore, we can access the shadow root via this reference.
Closed mode
Opposed to the open mode, the closed mode does not store a reference. We will have to create our own storage and retrieval way using either a Weak Map or an Object if we use the closed mode.
const shadowRoots = new WeakMap();class MyComponentClosedRoot extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'closed' });
shadowRoots.set(this, shadowRoot);
} connectedCallback() {
const shadowRoot = shadowRoots.get(this);
shadowRoot.innerHTML = `<h1>Hello!</h1>`;
}
}
Now that we have created a Shadow DOM, let’s look at how to attach and apply styles to the Shadow DOM nodes.
We will be using the
open mode
here onwards.
Attaching styles in the Shadow DOM
class MyComponentOpenRoot extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.close = this.close.bind(this);
}
connectedCallback() {
const { shadowRoot } = this;
shadowRoot.innerHTML = `<style>
.wrapper {
opacity: 0;
transition: visibility 0s, opacity 0.25s ease-in;
}
.overlay {
height: 100%;
position: fixed;
top: 0;
right: 0;
}
button {
cursor: pointer;
font-size: 1.25rem;
}
</style>
<div class="wrapper">
<div class="overlay"></div>
<div>
<button class="close">✖️</button>
<h1 id="title">Hello world</h1>
<div id="content" class="content">
<p>This is content outside of the shadow DOM</p>
</div>
</div>
</div>`;
}
}
All the styling required for the Shadow DOM nodes can be specified in the above manner. These styles will only apply to nodes inside the Shadow DOM as they are attached to the shadow root.
The Shadow DOM will be shown as below in the Chrome Dev Tools DOM elements.
So far, Shadow DOM seems very appealing. Finally, we have a way to scope out CSS without any hacks!
Using Shadow DOM in Practice
- There are alternatives to Shadow DOM, such as styled-components. However, since styled-components have CSS in JS, it is less performant than Shadow DOM. But if you already have styled-components in your project, you can stick to it rather than moving to Shadow DOM for style encapsulation.
- Even though Shadow DOM provides some powerful tools, we should not use it always. For example, if you have an
<form>
element in your DOM, you shouldn’t use Shadow DOM for components inside the form, such asinput
ortextarea
. This is because you cannot query nodes inside the shadow tree from the outside. Therefore, theform
will ignore the components where Shadow DOM is used. - If you want to style your component using a global CSS stylesheet, you should avoid using Shadow DOM in that particular component, as the global styles will not be applied if you use it.
- You should handle event propagation with care if you are using Shadow DOM in your component, as the use of UI events is different in and out of the shadow boundary.
- If you are using many third-party plugins or integrations in your application, you should be aware that some of these may not work with components that have Shadow DOM as they are not built to deal with it.
- Shadow DOM supports
<slot>
elements where you can render components from the light DOM to your Shadow DOM positions. When<slot>
is used, the browser performs composition and renders light DOM elements in corresponding slots in the Shadow DOM. - There are more ways of declaring a Shadow DOM as well. For example, Declarative Shadow DOM, which brings the Shadow DOM to the server. This is an exciting area to explore if you are interested in learning more about the Shadow DOM.
- Encapsulated styling is essential when sharing components between different web projects using tools like Bit. A reusable component should be context-agnostic - which should include preventing any possible styling/naming conflicts in possible hosting environments. For example, the following styled component I have shared on Bit can be used in any project without worrying about style conflicts.
Comments
Post a Comment