Open justinfagnani opened 1 year ago
This is a nice and thorough list. Is this issue meant to serve only as snapshot in time of open questions, or an (evolving) index of supporting issues/proposals, or is it (also) to serve as a place to discuss possible solutions/pose other questions?
We definitely need to collect more open questions. I'm not claiming this list is complete.
To the SSR category I would add:
How much (if any) of the SSR/SSG generated element(s) can be optionally used to define the custom element -- can the rendered DOM be used to take a snapshot and turn it into the template? If so, does it make sense to standardize some markers (like processing instructions) indicating things that can be removed/added during that process?
@bahrus I'm not completely following. I think this is covered by the hydration question, no? Is it just more specifics there on how hydration is achieved?
Maybe.
When I look at some of the proposals for what declarative custom elements might look like:
<definition name="percentage-bar">
<template shadowmode="closed">
<div>...</div>
<style>/*...*/</style>
</template>
<script type=module>
export default class MyEl extends HTMLElement { /*...*/ }
</script>
</definition>
... it's unclear to me whether that definition is intended to serve dual roles, or a single role. Dual roles would be not only does it define the structure of the custom element, but it would also be rendered directly in the live DOM tree as one instance of the custom element.
A single role would mean that the SSR generated content would automatically become ShadowDOM(ed) (already possible today in Chrome and Safari), but without that separate definition tag added somewhere, it would remain static HTML, and not morph into a web component.
I make that distinction clear (in my mind) here. Examples 1,2, 4, 5, the defining element serves dual roles. Example 3, that uses a template to define the element, doesn't. Example 3 has fewer parts to resolve, as there's no issue with how to construct the template for repeated renderings, for starters.
It's not clear to me whether I'm the only one who thinks serving dual roles is something worth pursuing, or if everyone is assuming that that goes without saying?
Definitions are just that - only definitions. They are not instances. Your example would only create an instance when there's a <percentage-bar>
element.
<template shadowrootmode>
does not belong inside a definition. Shadow roots are for instances. A definition would have a plain <template>
(possibly with a new attribute opting into the expression syntax).
That's what I thought, looking at that example of the percentage-bar. I guess, then, I'm the only one who thinks it is worth pursuing making the first instance also serve as a definition, as the second link I provide demonstrates.
Does my open question make sense to you now? I think you are saying no, by definition, the definition should not also be an instance. I.e. my open question has been opened and shut :-). I agree taking the stance you are taking simplifies things. Maybe I can reraise the possibility once that's accomplished (baby steps).
I personally think there should be a clear line between definitions and instances.
Making an instance also a definition raises all kinds of issues around expressions, conditionals, and control flow. They might be solvable, but I believe if they are that it could be added later. It could also probably be done in user-land by patching up the element definition based on the first instance's DOM.
What is the use-case for having a definition also be an instance? This would be quite unexpected to me personally. It also makes rendering complicated. It's easy to add a definition at the top of the body tag. If it were an instance I would need to keep track of how many times a tag has been rendered and then do some special rendering so that the definition is added the first time and then never again.
There are some web components that may contain quite a bit of HTML, and aren't even guaranteed to appear more than once on the page.
Consider how much html is behind this period table, for example. Expand the pug to generate the full html to see what I mean. Other examples: Calendars, Calculators, Chessboards.
So not supporting this would double the payload, for a single instance, especially if there's no dynamic logic to generate the HTML.
I also think it makes quite a bit of sense when writing to HTML to "define" the HTML markup for something, previewing what you are generating as you type, then give it a name, and reuse it, similar to defining a variable when coding. That's what my POC was meant to convey.
Another example would be the hamburger menu, which might likely be 1% JavaScript, 10% HTML, 89% CSS. We know we will need one instance for the top menu. We might, later, need to reuse the menuing capabilities it provides by some pieces of the page, lazily loaded. Again, seems like we are doubling the payload to accommodate complex scenarios (lots of dynamic content), at the expense of simple, static-ish scenarios. I'm perfectly fine doing one first, holding off on the other, but I just wanted to provide my use cases (there may be others).
I don't understand how this doubles the payload. You would define all of the HTML in the <definition>
tag and then when you use it it's just <periodic-table></periodic-table>
. Having the definition first means you don't need to include the DSD (correct me if I'm wrong here @justinfagnani).
That should be correct. A declarative element would have a template with some sort of binding syntax in it so that the instances used afterwards simply render using the template declared as part of the original definition.
In the context of streaming declarative ShadowDOM (which is the context in which I raised the issue) you would have the definition plus at least one instance rendered to by the server.
If we either:
Can we open a new issue for this discussion?
@justinfagnani In creating this issue, did you review/aggregate everything from the TPAC2022 report? If not, I can go over that and find anything that's missing. Just wanted to check first. Let me know.
@EisenbergEffect I built this list myself. I'm not sure we ever covered such a list of requirements for declarative custom elements in WCCG. If you know of anything missing, I'll add it.
@justinfagnani I'll do a quick review of what I have on my list and if I find anything not covered I'll drop it here. Adding this to my todo list. Probably will be a week or so before I get around to it.
The templating approach seems to me to be incompatible with server language agnosticism. This is a fundamental feature of the web that we should preserve at all costs. Instead of approach DOM manipulation from the perspective of templating; which inevitably leads towards "isomorphism" (the attempt to have the exact same code run in server and client), we should look to CSS which already successfully enhances HTML regardless of what backend technology was used to create it.
The templating approach seems to me to be incompatible with server language agnosticism.
I don't agree with this statement at all.
How does one write a DCE on the server in Python and then enhance in JS on the client without writing the same thing twice?
There are two options:
One, you write the DCE in the HTML served by your Python or Ruby or whatever server, and you use the component definition as a macro by reusing the custom element tag you just defined.
The other is to implement deep-SSR by implementing the standardize HTML template system and expression language in your server language of choice. I would absolutely expect to see HTML template evaluators in the major programming languages - in fact, why would you expect not to?
It's more than just reusing the template, you need to reuse the logic used to turn the template into HTML. Otherwise you are going to repeat the logic.
Client templates are a subset of the same server templates. For example say you have a template of:
<div>{a} <span>{b}</span> {c}</div>
Where only b
changes in the client. Because it's in the same template you need to use the same logic to generic a
and c
as well. This is, of course, wasteful, as these values do not change. It also forces you to write the same logic twice, in both languages.
Since no one wants to write the same code twice, in practice almost no one actually does this. Instead they choose JS and run the same code in both environments.
I think this is creating noise for this issue.
If you think that WICG should not pursue declarative custom elements and/or template instantiation, I suggest that you open an separate issue stating your argument.
There's a section in this document about SSR that presumes the common JS-only pattern of hydration. This is the part I'm objecting to. This is the only issue where I've seen that proposed. If you want to make template instantiation a DOM API only and not touch light DOM or DSD then I have less of a problem with it.
@matthewp There are open questions that I've listed under the SSR section as a hopefully helpful way of organizing things. These have no answers proposed for them here as that is not the purpose of this issue. If you have a new open question, I'd be happy to add it to the list.
If you have an proposed answer to a specific question, I suggest opening a new issue for that.
For instance, from your comments, I suspect you might have an opinion on:
How do declarative elements handle an existing shadow root from declarative shadow DOM (hydration)?
If you want to propose an answer, like: the element should crash and fail to render because attachShadow()
will throw, or: the element should simply overwrite any server-generated content, feel free to do so.
I would try to give a list for most generic and essential questions. Only upon those answered, the full list has a sence.
At the Spring 2023 F2F we talked about the need to discover all the open questions and dependencies on other specs that declarative custom elements have, in order to use declarative custom elements to help discover, prioritize other work.
Here are some questions and spec dependencies I can think of:
Questions
<img src="/avatars/{{lastName}}/{{firstName}}.jpg">
will make erroneous requests iffirstName
andlastName
are set at the same time.input.value
?mixedCase
property names?this.getAttribute('name')
->this.attributes.name
)this
?<img src="/foo/{{bar}}.jpg">
whenbar
is nullwindow.location
which can be used to exfiltrate data in bindings?onclick="javascript:..."
)I'm sure there are many more questions than this, of course.
Probable Spec Dependencies
Use cases
Many of these questions need to be driven from use cases. We'll need to know what kind of elements we're trying to allow to be built. One of the most import questions there whether interactive elements are in scope. For example, can you build the basic counter element that's a common framework example? That alone requires event listeners and self-updating state.