Day 28: custom properties and web components

posted on

It’s time to get me up to speed with modern CSS. There’s so much new in CSS that I know too little about. To change that I’ve started #100DaysOfMoreOrLessModernCSS. Why more or less modern CSS? Because some topics will be about cutting-edge features, while other stuff has been around for quite a while already, but I just have little to no experience with it.


We already know that we can encapsulate styles within a web component and we know that web components inherit styles. Another interesting feature of web components in terms of CSS is that custom properties used in a web component can be modified from the outside.

Let's take this basic alert component.

Please confirm your e-mail address by clicking the link in the e-mail we just sent you.
class Alert extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});

    const styles = document.createElement('style');
    styles.textContent = `
      div {
        background-color: rgb(136 177 255 / 0.5);
        color: rgb(0 0 0);
        font-weight: bold;
        padding: 1rem;
      }
    `

    const content = document.createElement('div');
    content.innerHTML = `
      <slot></slot>
    `

    this.shadowRoot.append(styles)
    this.shadowRoot.append(content)
  }
}

customElements.define('matuzo-alert', Alert);
<matuzo-alert>
  Please confirm your e-mail address by clicking the link in the e-mail we just sent you.
</matuzo-alert>

Now, let's say we want to reuse this component, but convey a different importance visually. We could add attributes (props) for styling to the web component.

<matuzo-alert type="error">
  The amount must be a value between 1 and 16.
</matuzo-alert>
styles.textContent = `
  div {
    background-color: rgb(136 177 255 / 0.5);
    color: rgb(0 0 0);
    font-weight: bold;
    padding: 1rem;
  }

  host([type="error"]) div {
    …
  }
`

This works and it’s also sometimes the preferred way, but we could also open the component up by using custom properties.

const styles = document.createElement('style');
styles.textContent = `
  div {
    background-color: var(--alert-bg, rgb(136 177 255 / 0.5));
    color: var(--alert-color, rgb(0 0 0));
    font-weight: bold;
    padding: var(--alert-spacing, 1rem);
  }
`

What's happening here is that we set the background-color, color, and padding to a custom property. If the custom property isn't defined, it falls back to default value. The web component still looks the same, but we can now change its styling according to our needs by modifying custom properties without touching the component.

Default

Please confirm your e-mail address by clicking the link in the e-mail we just sent you.
<matuzo-alert>
  Please confirm your e-mail address by clicking the link in the e-mail we just sent you.
</matuzo-alert>

Error

The amount must be a value between 1 and 16.
.error {
  --alert-bg: rgb(255 119 119);
  --alert-spacing: 2rem;
}
<matuzo-alert class="error">
  The amount must be a value between 1 and 16.
</matuzo-alert>

Success

Settings saved successfully.
.success {
  --alert-bg: rgb(39 149 39);
  --alert-color: rgb(255 255 255);
}
<matuzo-alert class="success">
  The amount must be a value between 1 and 16.
</matuzo-alert>

See on CodePen

Further reading

Overview: 100 Days Of More Or Less Modern CSS