Skip to content

home-assistant-tutorials/05.toggle-card-with-shadow-dom

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 

Repository files navigation

Plain Vanilla JavaScript Toggle Card With Shadow DOM

toggle on toggle off

Encapsulate CSS into a shadow DOM


  • @published: May 2023
  • @author: Elmar Hinz
  • @workspace: conf/www/tutor
  • @name: toggle-card-with-shadow-dom
  • @id: tcwsd

You learn:

  • how to attach a shadow dom inside the constructor
  • how to clean up the lifecycle
  • how to get rid of the prefixes
  • how to migrate BEM methodology with nested CSS modifiers

Intro

Introducing the usage of the shadow dom. It encapsulates the dom of the card, the nodes, the CSS, even the ids. This gives you some advantages. Organizing the CSS is getting easier. Also the lifecycle is getting easier to handle. The full shadow dom can already be created in the constructor, so that the elements are accessible early.

Prerequisites

  • tutorial 04: Plain Vanilla JavaScript Toggle Card
  • you know how to register the card.js as a resource
  • you know how to create the helper entity of type boolean aka toggle
  • you know how to add and edit a card
  • you know how to reload card.js after editing

Setup

Take the same steps as in the previous tutorial. Name the helper entity tcwsd this time.

configuration of the card

The code

Overview

The code of the previous tutorial is only slightly modified. The function doAttach() is modified to attach a shadow dom. The CSS is simplified.

Attaching the shadow dom

It's basically a single line of code making a huge difference.

Before:

    doAttach() {
        this.append(this._elements.style, this._elements.card);
    }

After:

    doAttach() {
        this.attachShadow({ mode: "open" });
        this.shadowRoot.append(this._elements.style, this._elements.card);
    }

The attachment of the shadow dom is straight forward. Now you are allowed to do the attachment already from the constructor.

    constructor() {
        super();
        this.doCard();
        this.doStyle();
        this.doAttach();
        this.doQueryElements();
        this.doListen();
    }

Simplifying setConfig()

setConfig() can now focus upon it's own tasks.

Before:

    setConfig(config) {
        console.log("ToggleCardVanillaJs.setConfig()")
        this._config = config;
        if (!this._isAttached) {
            this.doAttach();
            this.doQueryElements();
            this.doListen();
            this._isAttached = true;
        }
        this.doCheckConfig();
        this.doUpdateConfig();
    }

After:

    setConfig(config) {
        this._config = config;
        this.doCheckConfig();
        this.doUpdateConfig();
    }

Dropping BEM methodology

The CSS is encapsulated now. We can drop the block aka prefix and stay with elements and modifiers. Exploiting recent CSS advancements we can drop composed class names at all.

In CSS the BEM class .tcws-error-hidden becomes .error.hidden. I still recommend to use the combination of element and modifier .error.hidden over the naked modifier .hidden. Even the latter becomes less error prone as the shadow dom is rather small.

    .error { text-color: red; }
    .error.hidden { display: none; }

Some browsers already support nested CSS. CSS will become pretty organized.

    .error {
        text-color: red;
        &.hidden { display: none; }
    }

The HTML is adjusted accordingly.

    doCard() {
        this._elements.card = document.createElement("ha-card");
        this._elements.card.innerHTML = `
                <div class="card-content">
                    <p class="error error hidden">
                    <dl class="dl">
                        <dt class="dt"></dt>
                        <dd class="dd">
                            <span class="toggle">
                                <span class="button"></span>
                            </span>
                            <span class="value">
                            </span>
                        </dd>
                    </dl>
                </div>
        `;
    }

Releases

No releases published

Packages

No packages published