It lurks in the shadows…creating hidden trees…playing by a set of its own rules…it’s…

SHADOW DOM!

But, what exactly is Shadow DOM? Put simply, Shadow DOM is an API that is part of the Web Component model. It helps to isolate component internals, protecting users from breaking changes and global CSS.

I’d like to think we here at Ionic know a thing or two about Shadow DOM, as we recently made the move to using it in Ionic 4, and with that, received a lot of questions from the community on just what the heck it is. There’s a lot of information (and misinformation) floating around about Shadow DOM, so let’s go through the API and see what it actually does.

What is Shadow DOM?

Shadow DOM sounds like it could be a complicated API, but it’s actually built on two simple ideas, isolation and location. Need to create a bit of DOM that is isolated from the global scope? Shadow DOM is here to help. Need to specify the exact location of a piece of DOM? Shadow DOMs scope API is what you need!

It can be helpful to think of components that use Shadow DOM as modules for HTML. Markup and styles are isolated to their own DOM tree, and removed from the global context. This enables us to unlock some pretty incredible benefits:

Isolated DOM

Isolated CSS

Simplified CSS Rules

Typically, Shadow DOM is associated with Custom Elements, but you can still use Shadow DOM with regular HTML elements. In fact, most browsers use Shadow DOM without you even noticing. For example, every time you use video tag, you’re actually using an element that has Shadow DOM. Think about it, devs just need to write the <video> element, supply a video source, and set a few attributes on the HTML.

<video src="https://some-video.mp4" controls></video>

But parts of the video element, like video controls, are automatically created. Developers don’t actually have to provide their own play button, progress bar, volume control, they’re automatically provided. This is Shadow DOM in action!

Now let’s use this bit of Ionic markup:

<ion-content> <ion-item> Hello World </ion-item> <ion-button> I'm a button </ion-button> </ion-content>

This markup is pretty simple, all we’re doing is rendering an item and a button. Nothing too special right? Well, let’s look at the dev tools

Woah, where did all this come from? This is Shadow DOM working in Ionic’s components. All the internals of ion-item and ion-button can be abstracted away from the user, meaning all they need to know is the tag name, and is automatically handled by the browser. In short…it’s magic!

For Slots, we can provide all the entry points up front. For example, ion-item has three slots, Start, End, and the default. By using Shadow DOM, we can keep their location right where we want them to be, vs having users write their markup in a specific order. So there could be an item written as:

<ion-item> <ion-icon name="home" slot="end"></ion-icon> Hello World </ion-item>

And Shadow DOM and slots would know how to correctly render this.

Ok, so what’s the big deal? Ionic v3 already did something similar using Angular’s Content Projection. While Angular’s Content Projection is available, V4’s approach uses standardized APIs that all browser vendors have agreed on. Meaning, there’s no extra code needed at run time. By using the Shadow DOM, we get all the benefits of content projection, but built into the browser.

From a component author’s perspective, we can now author Ionic’s components in a way to prevents most breaking changes. Since we can control where and how a component’s content can be rendered, we’re able to prevent many mistakes and harden user’s code.

With the component’s internal markup isolated, updates and improvements can be made to any component, without the end user needing to update their code.

This is the real reason why Shadow DOM is being used in Ionic.

Parting Words

While Shadow DOM has been around for a few years now, it’s reached the point where we can finally take advantage of it in modern browsers. For the older browsers that are still around, we got them covered too, with on-the-fly polyfills being loaded. If a browser doesn’t support native Shadow DOM, we can polyfill it on-demand. However, if a browser already has native support, Ionic never downloads and parses any unnecessary polyfill JavaScript, but rather just uses the built in API.

It’s one of the many new exciting features under the hood in Ionic 4, and makes maintaining the Framework much easier. And as a result, we’ve been able to drastically reduce the amount of CSS code required for users’ apps because of its simplicity. Less code === Faster apps.

On top of that, devs building with Ionic don’t need to worry about potential breaking changes. Smaller CSS that is easier to maintain, and markup that has fewer breaking changes… what’s not to love about Shadow DOM?!

In a future post, we’ll look at how theming is done in V4 now that all of Ionic’s styles are isolated…