Search code examples
javascriptweb-componentshadow-domcustom-element

User's style for vanilla JS webcomponent


I have this component:

index.html

<html>

<head>
    <title></title>
    <meta charset="UTF-8">
    <script src="clock.js"></script>
    <style>
        clock-digital div {
            background-color: green;
        }
    </style>
</head>

<body>
    <clock-digital></clock-digital>
</body>

</html>

clock.js

customElements.define('clock-digital', class extends HTMLElement {

    constructor() {
        super();
        var shadowRoot = this.attachShadow({
            mode: 'open'
        });
        this._clockID = setInterval(function () {
            var currentdate = new Date();
            var hours = ( (currentdate.getHours() < 10) ? '0' : '') + currentdate.getHours();
            var minutes = ( (currentdate.getMinutes() < 10) ? '0' : '') + currentdate.getMinutes();
            var seconds = ( (currentdate.getSeconds() < 10) ? '0' : '') + currentdate.getSeconds();
            shadowRoot.innerHTML = `
            <style>
                div {
                    display: inline-block;
                    width: 65px;
                    text-align: center;
                    background-color: whitesmoke;
                    font-style: italic;
                    border: 1px solid lightgray;
                    border-radius: 3px;
                    box-shadow: 2px 2px 3px;
                }
            </style>
            <div>
                ${hours}:${minutes}:${seconds}
            </div>`;
        }, 500);
    }

});

I want that user of component can define his style on clock. I tried with:

<style>
        clock-digital div {
            background-color: green;
        }
    </style>

but it doesn't work. Should I use the slot tag somewhere in shadow root? What is best practice to achieve that?


Solution

  • You could expose CSS properties in your custom element that could be set externally.

    In your example, your element could define --clock-background-color, which sets the div's background color:

    shadowRoot.innerHTML = 
      `<style>
         div {
           background-color: var(--clock-background-color, whitesmoke);
           /* ... */
         }
       </style>
       <div>
         ${hours}:${minutes}:${seconds}
       </div>`;
    

    Then, your element's users could change the background color to green with this:

    <style>
      clock-digital {
        --clock-background-color: green;
      }
    </style>
    <clock-digital></clock-digital>
    

    codepen

    Note the Codepen demo uses Web Components polyfills for non-Chrome browsers, but you can comment it out to see that it still works vanilla in Chrome.