Cory Rylan

My name is , Software Developer and Speaker. Building Design Systems and Web Components.

Follow @coryrylan
Web Components

Simple CSS Custom Property APIs with Web Components

By Cory Rylan

- 2 minutes

Web Components provide a built-in Component model for the Web. Web Components also provide CSS style encapsulation via the Shadow DOM APIs. With Shadow DOM the CSS for our Web Components is completely encapsulated to the component and will not leak to the global scope. Global CSS also will not apply to Web Components using Shadow DOM. This is an excellent feature for CSS maintenance as it prevents us from accidentally styling elements unintentionally.

With Shadow DOM, however, there is a tradeoff. We sometimes need to expose a way to customize or theme our Web Component. We can do this with CSS Custom Properties. Custom Properties let us define dynamic variables that can be shared within our CSS. These properties are shared between the global scope and Shadow DOM. Let's take a look at a simple example.

import './style.css';

const template = document.createElement('template');
template.innerHTML = `
  <style>
    :host {
      --background-color: #ccc;
    }

    div {
      background-color: var(--background-color);
      padding: 24px;
      display: block;
      font-family: Helvetica, Arial, "Lucida Grande", sans-serif;
    }
  </style>

  <div>
    <slot></slot>
  </div>
`;

class UIBox extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
}
 
customElements.define('ui-box', UIBox);

In this simple box Web Component, we create a div with some basic styles. This div can change its background color via a custom property called --background-color.

ui-box {
  --background-color: red;
}

This works as expected, and the box background will be styled red. We can think of CSS Custom Properties as a public API for theming our component. Because of Shadow DOM, the customization is narrowed down to only the properties we choose to expose. However, we can give some more flexibility without expanding our API surface.

import './style.css';

const template = document.createElement('template');
template.innerHTML = `
  <style>
    :host {
      --background: #ccc;
      --padding: 24px;
    }

    div {
      background: var(--background);
      padding: var(--padding);
      display: block;
      font-family: Helvetica, Arial, "Lucida Grande", sans-serif;
    }
  </style>

  <div>
    <slot></slot>
  </div>
`;

class UIBox extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
}
 
customElements.define('ui-box', UIBox);

In this example, we have replaced --background-color with --background and added a --padding option as well. By changing our property to use a shorthand property, we enable any number of shorthand values rather than being limited to a single value.

ui-box {
  --background: content-box radial-gradient(crimson, skyblue);
  --padding: 0 12px;
}

While a small change, our theming API is more flexible without increasing the maintenance cost of adding additional CSS Custom Properties. Check out the working demo below!

View Demo Code   
Bluesky Facebook LinkedIn Email
 

Related Posts

StencilJS

Create your first Web Component with Stencil JS

Create reusable UI components that will work in any JavaScript Framework using StencilJS a Web Component framework.

Read Article
StencilJS

Using Stencil Web Components in Angular and the Angular CLI

Learn how to publish a Stencil Web Component and use in an Angular CLI app.

Read Article
Web Components

Introduction To Web Components

A tutorial on building reusable UI components in any JavaScript framework using Web Component APIs.

Read Article