Search code examples
web-componentlit-element

Adding classes to slotted elements (?)


I've run into an awkward scenario while working on a lit-element component library.

The component I'm currently working on is an "alert". Within it's render template it has two named slots: "title" and "text".

<div class="alert-item-content">
   <i class="alert-item-icon"></i>
   <slot name="title"></slot>
   <slot name="text"></slot>
</div>

The elements assigned to those slots will potentially be any valid text tag (h1, h2, h3, etc.). I need to target the slots' assigned element to A) remove/reset global styles that affect them and B) add specific styles that are fundamental to UI requirements.

So, in other words, a person might provide an 'h5' because it is semantically correct but, as an alert title, it should always appear the same.

Example:

<mult-alert>
  <mult-alert-item>
    <h5 slot="title">Notice Me</h5>
    <p slot="text">
      This is something you should probably
      <a href="#">pay attention to</a>
    </p>
  </mult-alert-item>
</mult-alert>

The only solution I could come up with so far is to query the assigned elements in shadowRoot and add a class to them. It does work but feels gross. This is the relevant code from the component for that:

addClassesToSlottedNodes() {
  [{
    slotName: 'title',
    className: 'alert-item-title'
  }, {
    slotName: 'text',
    className: 'alert-item-text'
  }].forEach(n => {
    const slotNodes = this.shadowRoot
      .querySelector(`slot[name="${n.slotName}"]`)
      .assignedNodes();

    if (slotNodes && slotNodes.length) {
      slotNodes[0].classList.add(n.className);
    }
  })
}

Is there a better way to target these slotted elements without adding classes this way? Or, is there a cleaner way to add the class in the component template?


Solution

  • Inside the Custom Element template for the Shadow DOM, you could use the ::slotted() CSS function to set the CSS style of the slotted elements:

    <style>
        slot[name=title]::slotted(*) {
            font-size: 20pt ;
            font-weight: bold ;
            color: red ;
        }
        slot[name=text]::slotted(*) {
            font-family: Arial ;
        }
    </style>
    <div class="alert-item-content">
        <i class="alert-item-icon"></i>
        <slot name="title"></slot>
        <slot name="text"></slot>
    </div>