How can I style parts which have been slotted into a web component?
My goal is to create 'functional' components which only renders some parts based on the state that they are in (using Redux). These components are then rendered in a container. This containers knows which kinds of children it can expect and should style the parts from the children accordingly.
An example is a feed of posts, in which all posts are the same web component which only render some parts. Then there is a grid feed component and renders these posts in a grid. Another chronological feed might simple render the posts underneath each other. In both cases the post itself is not aware in the context that it is in. I want the container component to be responsible for the layout/styling.
I kinda want the inverse of host-context. With host-context the post component knowns in which containers it can be in and style themself accordingly. But I would like to keep my post component purely functional without any styling. (and host-context is not well supported)
This snippet shows my attempts to make the title of the post red when it is in a my-grid element. But none of my attempts work.
customElements.define('my-grid', class extends HTMLElement {
constructor() {
super().attachShadow({
mode: 'open'
})
.append(document.getElementById(this.nodeName).content.cloneNode(true));
}
});
customElements.define('my-post', class extends HTMLElement {
constructor() {
super().attachShadow({
mode: 'open'
})
.append(document.getElementById(this.nodeName).content.cloneNode(true));
}
});
<template id="MY-GRID">
<style>
:host {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
grid-auto-rows: minmax(auto, 100px);
}
::slotted(my-post) {
display: contents;
}
/* Attempts at styling the parts withing the slotted posts */
my-post h1,
::slotted(my-post) h1 {
background-color: red;
}
my-post ::part(test),
::slotted(my-post) ::part(test) {
background-color: red;
}
my-post::part(test),
::slotted(my-post)::part(test) {
background-color: red;
}
</style>
<slot></slot>
</template>
<template id="MY-POST">
<h1 part="title">Title</h1>
</template>
<my-grid>
<my-post></my-post>
<my-post></my-post>
<my-post></my-post>
<my-post></my-post>
<my-post></my-post>
</my-grid>
You had multiple issues
title
and test
for your ::part(x)
references<my-grid>
<my-post></my-post>
<my-post></my-post>
<my-post></my-post>
<my-post></my-post>
<my-post></my-post>
</my-grid>
must be styled from its container.... in this case the main document DOM
So all global style required is:
my-post::part(title) {
background: red;
}
You can not do this in <my-grid>
because <my-post>
is not inside <my-grid>
lightDOM
<my-grid>
can not style its slotted content (only the 'outer' skin with ::slotted
)
I added extra styling, slots and a nested <my-post>
element to make things clear
<script>
class GridElements extends HTMLElement {
constructor() { super().attachShadow({mode: 'open'})
.append(document.getElementById(this.nodeName).content.cloneNode(true)) }}
customElements.define('my-grid', class extends GridElements {});
customElements.define('my-post', class extends GridElements {});
</script>
<template id="MY-GRID">
<style>
:host{display:grid;grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)) }
::slotted(my-post) { background: green }
</style>
<slot></slot>
</template>
<style id="GLOBAL_STYLE!!!">
body { font: 14px Arial; color: blue }
my-post::part(title) { background: red }
my-grid > my-post::part(title) { color: gold }
my-post > my-post::part(title) { background:lightcoral }
</style>
<template id="MY-POST">
<h1 part="title"><slot name="title">[Title]</slot></h1>
<slot>[post body]</slot>
</template>
<my-grid>
<my-post><span slot="title">One</span></my-post>
<my-post>Two</my-post>
<my-post></my-post>
<my-post>
<my-post><span slot="title">SUB</span></my-post>
</my-post>
</my-grid>
my-grid
I added a my-post
to the my-grid
lightDOM:
See: https://jsfiddle.net/WebComponents/75xgt3y2/
So if you still want to declare the content in the main DOM, but style from my-grid
, you have to move elements from the main DOM to my-grid
lightDOM:
connectedCallback(){
this.shadowRoot.append(...this.children);
}
Also see exportparts for crossing multiple shadowDOM boundaries: