I have developed a HTML web component, with a slot that uses a HTML theme
attribute to enable spacing out the child elements (by applying a margin
to them). I want to control the size of the spacing using a CSS custom property, --spacing-size
, set on the HTML style
attribute of each component instance.
class VerticalLayout extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
}
connectedCallback() {
this.shadowRoot.innerHTML =
`<style>
:host {
display: flex;
flex-direction: column;
}
:host([theme~="spacing"]) ::slotted(:not(:first-child)) {
margin-top: var(--spacing-size);
}
</style>
<slot></slot>`;
}
}
customElements.define("vertical-layout", VerticalLayout);
I run into problems when I add an instance of my component into the slot of another instance, because my CSS targets slotted elements to give them the margin. Since in this case the slotted element is my component, with a value of --spacing-size
meant for its children, it gets the margin meant for its children and not the margin needed inside the parent.
<vertical-layout theme="spacing" style="--spacing-size: 2em;">
<div>No margin on this, it is the first child</div>
<!-- I want a 2em margin on the next element (the size -->
<!-- from the layout it sits within), but it gets 0.2em -->
<!-- because --spacing-size has been redefined -->
<vertical-layout theme="spacing" style="--spacing-size: 0.2em;">
<div>No margin on this, it is the first child</div>
<div>0.2em margin above this</div>
</vertical-layout>
</vertical-layout>
I have created a codepen. See in the codepen I have overcome the issue by adding a second custom property, --parent-size
. The codepen illustrates the spacing I expect on the layouts, but is there a clever way to achieve the same behaviour with just one custom property?
See in the codepen, an additional complication is that I am explicitly setting the --spacing-size
to a default value in the CSS, to be applied when the theme is turned on but a size is not specified. I suppose this could make it pretty difficult to inherit whatever the value is in the parent...
I feel like the :host-context()
selector might be the answer, but I can't quite grasp how I could use it (and, since Safari doesn't support that, I would have to look for another solution anyway).