← Back
Launch Notes

Not Today, ShadowDOM. Not Today.

  • Engineering
An image of a bright white arrow crashing into an exploding maroon star

When you’re building an SDK for in-app chat and commenting like we are, you expect that your UI components are going to live alongside other components in an application. Every UI library has this challenge. In our case, it’s a bit more complex because we support React and also offer our UI components in vanilla JavaScript so that they work with other frameworks like VueJS or Ember or Angular.

A big concern for us was that our CSS would bleed into the CSS of the page or that our user’s CSS would cause issues with our components’ layouts. So, we did what software engineers do. What follows is the story of how we ditched the shadowDOM in 6 short acts.

Act I: The ShadowDOM will save us

The Great Gatsby Toasting to his own brilliance and privilege

Like many software teams, we looked for an existing technology solution to our problem. Reading the W3C spec for WebComponents and ShadowDOM, it looked like a great match.

  • We need a way to prevent our CSS from bleeding into the page. ✅
  • We need a way to prevent the page’s CSS from bleeding into our components. ✅
  • We need a way to encapsulate our logic and give a clean API. ✅

This is going to be great.

Act II: The ShadowDOM strikes back!

Gru's plan to prevent developers from styling their own pages

So, it turned out the ShadowDOM does indeed do what it says on the tin. Very well in fact. Actually – it’s so good at what it does that you can’t actually style anything.

If what you want is completely controlled UI with absolutely no customizability, then ShadowDOM is probably a good choice. Since we offer an SDK and UI library to developers, it turns out to be a pretty bad choice. It’s like a door with no door handle.

To make matters worse, when you use the developer console in Chrome or Firefox, Shadow Root elements don’t look like normal elements. This means that developers have to learn how to debug their CSS all over again.

Screenshot of the developer tools when a Shadow Root is in the page

"What the hell is ':host' !?!"


A good rule of thumb for a developer-centric product like Cord is that if developers don’t know what your thing is… you’re not really doing the thing. So we learned.

Act III: CSS variables will save us!

A meme image of a car swerving off the highway

We weren’t ready to throw in the towel on having good isolation, so next we tried using CSS variables as a way to give just-enough control of the look and feel to the developers building with Cord. This worked pretty well at first. That is… until it didn’t.

Act IV: CSS variables are killing us

Woody and Buzz Lightyear meme

What we discovered was that it was never enough control. Every time we thought we’d given the right amount of CSS variables access, we discovered that developers were doing something juuuuust a bit outside of what we’d wired up variables for. By the tenth-out-of-ten customer integrations where we had to add new CSS variables, we could see this approach wasn’t working. If we kept going, we’d just end up rebuilding the entire CSS spec in CSS variables.

Act V: CSS Parts will save us!

Man in glasses with butterfly meme

You know how the old saying goes:

Fool me once, shame on… shame on you. Fool me… can’t get fooled again.” – Gandalf

Before we dove into using CSS Parts, we took it on a little spin. Turns out, CSS Parts don’t even make it off the run way.

cord-floating-threads::part(button):first-child {
  color: red;

A basic piece of CSS like this is unsupported by CSS Parts. Why? Because CSS Parts do not permit “structural” selectors (i.e. :first-child). Which makes sense if you’re trying to carefully protect your stuff from… everyone and everything. If you want to actually give developers the ability to use CSS as they know it, CSS Parts are basically unusable.

Since we’re building a UI library for chat and commenting, web developers have to be able to use the CSS they already know. If they can’t, we’re dead in the water. It’s a non-starter.

Act VI: The hero returns transformed

Man draws 25 in Uno

In the end, we stepped back and thought hard about whether or not we were solving a real problem with ShadowDOM. Sure, if we didn’t have it, there’s some risk of the page breaking us. And some possiblity of our stuff breaking the page. And if that happens…

Well, honestly, it doesn’t actually happen that much.

And if it does, if we don’t have a ShadowDOM, you can just use the standard Chrome/Firefox developer tools to debug the issue. Because it’s just run-of-the-mill vanilla CSS, debugging it is something every web developer already knows how to do. No learning curve.

But it is work. A lot more than 25 Uno cards worth of work. But I’m very proud to share that our team has moved this mountain already. We’ve migrated all of our key UI components for messaging, chat threads, and presence away from the ShadowDOM. Now we’re using vanilla CSS classnames and open WebComponents.

This means that when you’re a web developer using Cord, you get to use whatever CSS framework you like. All of your structural selectors like :first-child etc. all work. Whether you use SCSS or newer tech like Vanilla Extract, as long as you’re putting CSS rules into the page, you can style Cord’s UI to your liking. It was hard work to get here, but we’re building a killer SDK for adding chat and commenting and real-time collaboration to any app. Making app developers’ lives easier is worth working hard for.