Styling Web Components

  • Last updated on 14th May 2022

Introduction

In this post, we’ll learn how to style our web components. Checkout my previous post on how to create them.

Styling web components

As with our css declarations for our HTML, we have access to different css methods and properties.

We’ll start with our completed html and javascript solution to creating web components from my last post, except now we’ve added global styles.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Web Components</title>
    <!-- Styles -->
    <link rel="stylesheet" href="./main.css" />
    <script src="./monsters.js" type="module"></script>
  </head>
  <body>
    <header>
      <h1>Style Web Components</h1>
    </header>
    <main>
      <h2>Our Web Component Below</h2>
      <monster-encounters>
        <h3 slot="title">Monster Types</h3>
        <ul slot="list">
          <li>Nymph</li>
          <li>Bear</li>
          <li>Stone Golem</li>
          <li>Air Elemental</li>
        </ul>
      </monster-encounters>
    </main>
  </body>
</html>
/* In main.css */
* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}
html {
  font-size: 2rem;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
    'Open Sans', 'Helvetica Neue', sans-serif;
}
header,
main {
  padding: 1rem 2rem;
}
const template = document.createElement('template')
template.innerHTML = _
  <div>
    <h3>Monster Encounters</h3>
    <slot name="title">Default text if not title slot used in HTML</slot>
    <slot name="list"></slot>
  </div>
_

class Monsters extends HTMLElement {
  constructor() {
    super()
    const shadowRoot = this.attachShadow({ mode: 'closed' })
    let clone = template.content.cloneNode(true)
    shadowRoot.append(clone)
  }
}

customElements.define('monster-encounters', MonsterEncounters)
// <monster-encounters>

Properties and selectors

When we style our custom web components, we have access to different css selector and properties. To add styles to our template, we can use the style tag because we’re working with plain html, like below:

const template = document.createElement('template')
template.innerHTML = _
   <style>

    /* @import url(); */
   
    div {
      border: 1px solid red;
      padding: 2rem;
      margin: 2rem;
    }
    :host{
      /* for the shadow root */
      background-color: green;
      display: block;
    }
    :host(big-bang){
      background-color: black;
    }
    :host-context(main){
      background-color: pink;
    }

    ::slotted(h3){
      font-size: 2rem;
      color: grey !important;
    }

    /* In our case, applying properties to slot wouldn't work because the slot is itself being replaced by the h2 variable. It overrides it.   
    slot {
    }
    */

    /* Part wouldn't work with out current code. Although it works by referencing the attribute allowed by template in our main.css. Just like with slot below in our HTML, it would be declared the same way, except we would reference the part value (**(::part(VALUE_HERE)**) in the external css from where we want to apply our styles to.
    ::part(){
    }
    */

    h3{
      color: red;
    }
  </style>

  <div>
    <h3>Monster Encounters</h3>
    <slot name="title">Default text if not title slot used in HTML</slot>
    <slot name="list"></slot>
  </div>
_

Above, with @import url() we can build a component/theming system of stylesheets and import those to this component. Say, if we needed a common stylesheet for a standard every one of our components need to adhere to because they need a common look and feel.

Our :host selector lets us add properties to the shadow root itself. We have to include the display: block; property because without it, our elements are inline and won’t show. The :host selector lets us add properties to a specific web component, we need this if for example we have many components. host-context selector lets us add properties to the parent container of the current web component we’re working with, in this case we’re making the color red. It’s worth noting that :host and :host-context have the same level of specificity in our declaration. So whichever one is placed last, will be applied. In our case, background-color red.

With ::slotted(h2), we can add properties to the slots inside our web component. In our case, it’ll be h2. For the second property, our color value is being overriden by the parent color value penetrating through our shadow DOM from our main.css file in the beginning. Only a few properties are subject to do this. So we override those with important.

Below is how our simple Web Component will look:

final-web-component

Ending Thoughts

In this article, I discussed how to style web components for theming, the different properties and selectors available to us to style in our shadow DOM, and the style declaration precedence with some web component selectors. Hopefully, it has brought some more understanding of how we can use it to create a design system. Have a great day!

Resources

MDN Web Components