Hero image

Using Web Components

Web components have been native to the browser for many years now but each time I come back to them I forget the key rules around how to properly encapsulate them. Here are the three rules I re-learn and keep coming back to each time.

  1. Use the Shadow DOM
  2. Inline Styles
  3. Style Defaults

Rule 1. Use the Shadow DOM

To make sure you have full control over your component you should put the core elements into the shadow dom. This takes the elements out of the scope of the normal page, meaning existing rules on the page will not affect how your component styles by default.

Why would you want this? The short answer is existing components/pages you need to be compatible with.

Lets say you are tasked with creating a new card component for many pages to use. A really common pattern is to use three divs with the classes header, body, and footer. Unfortunately you know these classes are very common and will almost certainly already be used on the page with another component.

You could instead try to prefix your classes with something unique to your component, but you can never truly know that your classes are unique unless you check every page which is often impractical. Or the can stay the same but be encapsulate them in the shadow dom. This keeps the names simple and clear!

As a component creator you likely won't know much about existing pages or components, so lets imagine the card already exists on one of the pages our component will live on that you don't know about.

Existing Card Style
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio.

Because we didn't know all the pages or CSS styles that are existing, when we added our component which only uses the light dom, you can see we get both card styles at once.

Whereas using the same code, but having it in the Shadow DOM keeps us safe from this bleeding of styles.

In this case I think the style bleed isn't too bad, but if you consider every page may be using the same class names with different styles you can see how it would get out of control quickly. Best to be safe and put things in the shadow dom.

Rule 2. Inline Styles

When importing from a CSS file you will always get a Flash of unstyled content, so instead styles should be inlined whenever possible.

If you don’t know what that is, it is when the component renders to the page before the CSS styles are loaded in. The most noticable flash is when spacing/text locations are not the same which is what happens to the card component we made.

Using the exact same component as above, I have imported the css instead of having it inline. Pressing the button below will import the component to the page again which should at first render the component without the styles, then quickly apply them. You may need to click the button a few times to see the content flash.

Rule 3. Style Defaults

Most styles that you want to have defaults for can be done with CSS variable fallback values. This allows you to have an easily overwritten default through the following syntax: var(--named-variable, <fallback value>);.

For styles you want a strong default for, you can add the default to the :host selector. This will make it possible to override, but will require a more deliberate effort by the developer using your component.

This is because styles on the :host can be overwritten by styles that are applied directly to your component. Lets see what that means.

Firstly I have set the default colour of the card to be brown using the following css inline on the component itself. I have defined it on the host so you can see what I mean by strong defaults.

:host {
  --styled-text-color: brown;

This makes the card look as follows:

Lets say I want to override my component to have some grey text instead of brown. To do this I have created a simple class to set the css variable to the new value.

.grey-text {
    --styled-text-color: grey;

If I then add this class to the parent element of our component, you may expect it to change to grey - but lets see what actually happens:

Looks like nothing changed. This is because the css variable was defined as grey on the parent - but then defaulted back to brown on the component. The style will need to be updated on the element itself to be applied. This is what I mean by strong. It lets you be more deliberate with your styles.

If we instead apply the class to the web component element you can now see some nice grey text instead of what the component defined.

Note: This should only be used where you are very certain about your defaults - where only advanced usage should be able to override them.

What to read more? Check out more posts below!