I am building a flex custom element which needs to style its children as well as itself. It is a "stack" element that sets its children to have a uniform vertical spacing. (This comes from reading every layout)
The html looks like:
<test-stack> <h3>Hello World</h3> <p>I'm a paragraph</p> <div>And I'm a div with text</div> </test-stack>
The simplified custom element is:
class TestStack extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
}
render() {
this.shadowRoot.innerHTML = `
<style>
:host {
display: flex;
flex-direction: column;
justify-content: flex-start;
}
:host > * {
margin-block: 0;
}
:host > * + * {
margin-block-start: 2rem;
}
</style>
`
console.log('render', this.shadowRoot.innerHTML);
}
connectedCallback() {
this.render()
}
attributeChangedCallback() {
this.render()
}
}
customElements.define('test-stack', TestStack)
NOTE: The two selectors
:host > *
:host > * + *
.. obviously do not work but are examples of my trying to style all the children of a test-stack custom element.
So how does a custom element style it's children as well as itself? Flex is a good example of custom elements needing to do this. Maybe simply not possible with the shadow DOM?
With shadowDOM you have to slot lightDOM in a <slot>
slotted lightDOM is styled by the container it is in! In this case the main document
also see: ::slotted CSS selector for nested children in shadowDOM slot
And don't forget to read about: https://developer.mozilla.org/en-US/docs/Web/CSS/::part
If you want fancy styling inside shadowDOM, you have to add an extra <div>
container element.
Here are both methods in one Web Component:
you will ofcourse not copy the style, but style the DIV in the shadowRoot innerHTML
<style id="STYLE">
test-stack {
display: flex; flex-direction: column; justify-content: flex-start;
}
test-stack > * {
background: lightgreen; margin-block: 0;
}
test-stack > * + * {
background: pink; margin-block-start: .5rem;
}
</style>
<test-stack id="STACK">
<h3>Hello World</h3>
<p>I'm a paragraph</p>
<div>And I'm a div with text</div>
</test-stack>
<script>
customElements.define('test-stack', class extends HTMLElement {
constructor() {
super().attachShadow({mode: 'open'})
.innerHTML = `<style></style>
slotted: <slot></slot>
copied: <div></div>`
}
connectedCallback(){
// COPY STYLE AND HTML for demo purpose only
let style = STYLE.innerHTML.replaceAll("test-stack","div");
this.shadowRoot.querySelector("style").innerHTML = style;
this.shadowRoot.querySelector("div").innerHTML = STACK.innerHTML;
}
})
</script>