Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interaction between imperative slotting and declarative shadow DOM #967

Open
rniwa opened this issue Aug 27, 2022 · 16 comments
Open

Interaction between imperative slotting and declarative shadow DOM #967

rniwa opened this issue Aug 27, 2022 · 16 comments

Comments

@rniwa
Copy link
Collaborator

rniwa commented Aug 27, 2022

It's unclear how imperative slotting would work with declarative shadow DOM. Presumably, slot needs to remain empty until scripts become available.

I wonder if there is a way to serialize imperative slot API though. If there was a way to express imperative assignment using, for example, IDs, that would pave a way for declarative shadow DOM to avoid showing slot's fallback content while scripts are getting loaded.

@justinfagnani
Copy link
Contributor

There is an impedance mismatch here, somewhat similar to scoped registries, but more tractable to a purely declarative solution...

I think, like proposed for scoped registries, the slotAssignment option can be represented as an attribute on <template shadowroot to at the very lease keep the fallback content from showing until script takes over.

I wonder if there is a way to serialize imperative slot API though

The naive thing, and maybe even quite practical in SSR systems, is to write the slot attribute back onto the children - but we obviously don't want to modify children and don't have a way to switch from declarative to imperative slotting. Children don't have to have IDs, but element ordering should be pretty stable to use for SSR, so maybe <slot> can have an attribute that's the index of the child nodes assigned to it? This could allow text nodes to be assigned too.

@sashafirsov
Copy link

sashafirsov commented Aug 27, 2022

The hydration lifecycle which is now under development on libs level and preparing for proposal would give a proper concept on interaction of DSD and materializing its content. Rather trying to make an insulated niche DSD opinionated decision, perhaps better to adopt more generic pattern and make DSD capable and compliant with its requirements?

The hydrate option reflects the exact timing when it has a sense to apply as the slots as associated JS API.

Scripts availability also meant to be the subject for hydration, sometimes materialized further in component life cycle. Hence better not to make a decision instead of page developers. Let it be, let it be, let it be 🧑‍🎤

@rniwa
Copy link
Collaborator Author

rniwa commented Sep 28, 2022

@sashafirsov : I don't really follow what you're proposing. Are you saying that we should come up with a generic hydration mechanism for custom elements? That seems like an orthogonal issue. This issue is about how declarative shadow DOM can specify the slot assignment mode, which is determined at the time of shadow root creation and not dynamically changeable.

@rniwa
Copy link
Collaborator Author

rniwa commented Sep 28, 2022

@justinfagnani : So I guess a proposal is something like this:

<custom-element>
    <template shadowroot="closed" slotassignment="manual">
        <slot initialassignment="0"></slot>, <slot initialassignment="2 3"></slot>
    </template>
    <custom-child>hello</custom-chid>
    <custom-child>world</custom-chid>
    <custom-child>shadow</custom-chid>
    <custom-child>DOM</custom-chid>
</custom-element>

would yield: "hello, shadow DOM"

@justinfagnani
Copy link
Contributor

@rniwa that seems about right.

Would the index have to be into the child list, not just the child element list, though? So initialassignment="0" would be the text node with just whitespace before the first <custom-child>?

@rniwa
Copy link
Collaborator Author

rniwa commented Sep 28, 2022

Hm... I guess so. So the above example will be more like this:

<custom-element>
    <template shadowroot="closed" slotassignment="manual">
        <slot initialassignment="1"></slot>, <slot initialassignment="5 7"></slot>
    </template>
    <custom-child>hello</custom-chid>
    <custom-child>world</custom-chid>
    <custom-child>shadow</custom-chid>
    <custom-child>DOM</custom-chid>
</custom-element>

@justinfagnani
Copy link
Contributor

The unfortunate bit there is the sensitivity to whitespace. I wonder if, similar to assignedNodes() vs assignedElement(), if we'd want two attributes so that if you only want to slot elements it's not sensitive to whitespace.

<custom-element>
    <template shadowroot="closed" slotassignment="manual">
        <slot initialelements="0"></slot>, <slot initialnodes="5 7"></slot>
    </template>
    <custom-child>hello</custom-chid>
    <custom-child>world</custom-chid>
    <custom-child>shadow</custom-chid>
    <custom-child>DOM</custom-chid>
</custom-element>

@rniwa
Copy link
Collaborator Author

rniwa commented Sep 30, 2022

@justinfagnani : That works. We should also make us skip any non-digit tokens separated by spaces so that we can extend it in the future (e.g. mixing IDs, supporting non-direct child, etc...).

@annevk
Copy link
Collaborator

annevk commented Sep 30, 2022

@mfreed7 @yuzhe-han @smaug---- are you following this?

Nit: should we prefix all attributes on template with shadow?

@rniwa makes a good point that if we want to extend this in the future we should think carefully about the processing model. In particular if we want to allow new things with fallback.

@bathos
Copy link

bathos commented Oct 1, 2022

The unfortunate bit there is the sensitivity to whitespace. I wonder if, similar to assignedNodes() vs assignedElement(), if we'd want two attributes so that if you only want to slot elements it's not sensitive to whitespace.

My impression had been that declarative shadow’s use cases were tied up with some notion of producing/taking a “snapshot” which would likely not be maintainable without some kind of generative tooling (e.g. build tools if static or runtime libraries that don’t permit naive string concatenation if dynamic SSR). Since the concern expressed here seems to regard ergonomics, it made me wonder if there are additional motivating use cases where authors would be writing declarative shadows directly?

(Idea seems fine to me, just trying to get a sense of how much this is meant to be an “author feature” vs (metaphorically) “html bytecode”).

@sashafirsov
Copy link

sashafirsov commented Oct 2, 2022

@rniwa

Are you saying that we should come up with a generic hydration mechanism for custom elements? That seems like an orthogonal issue. This issue is about how declarative shadow DOM can specify the slot assignment mode, which is determined at the time of shadow root creation and not dynamically changeable.

It just seems like orthogonal. Once the hydration define the loading life cycle, the slots filling, imperative or declarative, has to be triggered as one of Web Component life cycle sequence item . I.e. without hydration, defining slots filling convention is premature. That could happen even before come to the browser, during build time or SSR, or way later on scroll or click event.

@rniwa
Copy link
Collaborator Author

rniwa commented Oct 5, 2022

So I can think of two different processing models.

  1. initialelements / initialnodes are features of HTMLSlotElement. In this case, initialelements / initialnodes are supported independent of declarative shadow DOM. This would mean that we'd have to define between when these attributes define the assigned nodes (probably until the first call to assign()?).
  2. initialelements / initialnodes are features of declarative shadow DOM. In this case, initialelements / initialnodes are only supported within a declarative shadow DOM, and it substitutes assign() function calls after shadow host's children had been inserted.

One annoying thing about (1) is that we'd have to monitor any changes to the shadow host's child nodes over time, and update the currently assigned nodes. For (2), we'd have to think about whether slot assignment will be dynamically updated as more of shadow host's child nodes get parsed (i.e. allow streaming) or not. If we were to dynamically update the assigned nodes, it would have the same issue as (1) in that we'd be effectively monitoring any changes to shadow host's child nodes.

@annevk
Copy link
Collaborator

annevk commented Oct 5, 2022

For 2 it would only be for the duration of the parse although the parser doesn't really have a generic "end tag seen" hook at which point we could remove the listeners. Or perhaps we could do it as part of the parser's insert operation...

@mfreed7
Copy link

mfreed7 commented Oct 7, 2022

My impression had been that declarative shadow’s use cases were tied up with some notion of producing/taking a “snapshot” which would likely not be maintainable without some kind of generative tooling (e.g. build tools if static or runtime libraries that don’t permit naive string concatenation if dynamic SSR). Since the concern expressed here seems to regard ergonomics, it made me wonder if there are additional motivating use cases where authors would be writing declarative shadows directly?

(Idea seems fine to me, just trying to get a sense of how much this is meant to be an “author feature” vs (metaphorically) “html bytecode”).

I’m also curious about the use case. Mixing imperative slot assignment with declarative shadow dom, on its face, sounds at least a little odd. If tooling is doing the SSR work, perhaps the generated HTML can just use normal declarative slot assignment? I.e. assign values to the <slot name=foo> and <div id=light-child slot=foo> to slot in the correct content?

@rniwa
Copy link
Collaborator Author

rniwa commented Oct 7, 2022

I’m also curious about the use case. Mixing imperative slot assignment with declarative shadow dom, on its face, sounds at least a little odd. If tooling is doing the SSR work, perhaps the generated HTML can just use normal declarative slot assignment? I.e. assign values to the <slot name=foo> and <div id=light-child slot=foo> to slot in the correct content?

The primary use case here would be SSR of imperative slot assignment, particularly those that re-order nodes. Since slot assignment mode is fixed per shadow root, imperative slotting doesn't work well right now with SSR and limits the use scenarios of imperative slotting. FWIW, I wouldn't imagine specifying the initial assignment this way would be a common developer experience.

@keithamus
Copy link
Collaborator

WCCG had their spring F2F in which this was discussed. Present members of WCCG reached a consensus to resolve this issue with the proposed two attributes on <slot>.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants