Cory Rylan

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

Follow @coryrylan
Web Components

Simple CSS Custom Property APIs with Web Components

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   
Twitter Facebook LinkedIn Email
 

No spam. Short occasional updates on Web Development articles, videos, and new courses in your inbox.

Related Posts

Lit Web Components

High Performance HTML Tables with Lit and CSS Contain

Learn how to easily create HTML tables in Lit with high performance rendering using CSS contain.

Read Article
Lit Web Components

High Performance HTML Tables with Lit and Virtual Scrolling

Learn how to easily create HTML tables in Lit from dynamic data sources.

Read Article
Lit Web Components

Creating Dynamic Tables in Lit

Learn how to easily create HTML tables in Lit from dynamic data sources.

Read Article