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?





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.








Image for post

Image for post




Structure of a Shadow DOM

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?





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





  1. Open mode

  2. 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




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.







Image for post

Image for post



So far, Shadow DOM seems very appealing. Finally, we have a way to scope out CSS without any hacks!



Using Shadow DOM in Practice




  1. 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 as input or textarea. This is because you cannot query nodes inside the shadow tree from the outside. Therefore, the form will ignore the components where Shadow DOM is used.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.











Image for post






Comments

Popular posts from this blog

How to create a blog in PHP and MySQL database - DB design

Secure React SPA using Keycloak with OpenID Connect in 2021

All About Software Architecture for 2021 Developers