I'm learning web components. When designing a custom element, I have to decide what is going to be hidden in the the shadow DOM. The remainder will then be exposed in the light DOM.
As far as I understand, the APIs allow two extreme use cases with different tradeoffs:
- hide almost nothing in the shadow DOM, most of the element's content is in the light DOM and in the element's attributes:
- this allows an HTML author to provide anything for the component to display without writing JS;
- this is close to the status quo regarding searchability and accessibility
- but there is little reward for the work involved; I add complexity with components but they don't encapsulate anything (everything is exposed).
- hide almost everything in the shadow DOM, the element's innerHTML is empty:
- this requires the element to be instantiated from JS;
- this locks usage a lot more because instantiating from JS is more strict (type-wise) than using HTML slots and attributes;
- this may be less searchable and accessible (I'm not sure whether this is the case);
I currently lean toward hiding everything in the shadow DOM for the following reasons:
- I intend to instantiate everything from JS. I'm not going to author pages in HTML manually. It would be more work to code both an HTML API and a JS API.
- It's less cognitive work to hide everything. I don't need to find a right balance about which information is visible in the light DOM.
- It's closer to most JS frameworks I'm familiar with.
Am I missing something?
Edit
Thank you, I am answered that it depends on the use case which partially answers my question. But I'm still missing an answer regarding the case I'm in: I'd rather not support slots for some of my components.
I'll add an example for each extreme of the spectrum:
<template id=light-email-view>
<div>
<div><slot name=from></slot></div>
<ul><slot name=to></slot></ul>
<h1><slot name=subject></slot></h1>
<div><slot name=content></slot></div>
<ul><slot name=attachements></slot></ul>
<div class=zero-attachment-fallback>no attachments</div>
</div>
</template>
<template id=shadow-email-view>
<div></div>
</template>
<script>
...
let view = document.createElement('shadow-email-view');
// this method renders the email in the shadow DOM entirely
view.renderFromOject(email);
container.appendChild(view);
</script>
In the first example, the component author has more work to do since they need to "parse" the DOM: they have to count attachments to toggle the fallback; basically, any transformation of input that isn't the browser copying an element from the light DOM into the matching shadow DOM slot. Then they need to listen for attribute changes and whatnot. The component user also has more work, they have to insert the right elements into the right slots, some of them non-trivial (the email content may have to be linkified).
In the second example, the component author doesn't need to implement support for instantiating from HTML with slots. But the component user has to instantiate from JS. All the rendering is done in the .renderFromObject
method by the component author. Some additional methods provide hooks to update the view if needed.
One may advocate for a middle ground by having the component offer both slots and JS helpers to fill those. But I don't see the point if the component isn't to be used by HTML authors and that's still more work.
So, is putting everything with the shadow DOM viable or should I provide slots because not doing so isn't standard compliant and my code is going to break on some user agent expecting them (ignoring older UAs that are not at all aware of custom elements)?