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?
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>