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 with declarative shadow DOM #914

Open
justinfagnani opened this issue Mar 12, 2021 · 33 comments · May be fixed by #915
Open

Interaction with declarative shadow DOM #914

justinfagnani opened this issue Mar 12, 2021 · 33 comments · May be fixed by #915

Comments

@justinfagnani
Copy link
Contributor

We need to specify how scoped custom element registries interact with declarative shadow DOM.

The fundamental behavior we need is for a declarative shadow root to not use the global registry when it's going to be used with a scoped registry once its host is defined. Then we need the host to be able to assign a scoped registry to a shadow root that's already been created.

This is accomplishable with an attribute that maps to an attachShadow() option to opt-out of the global registry, without yet having a scoped registry to use. In attachShadow() options, we can allow registry to be undefined. In declarative shadow DOM, we can introduce an attribute, like shadowrootregistry:

<template shadowroot="open" shadowrootregistry="">
  <some-scoped-element></some-scoped-element>
</template>

Next, we need to be able to add the ability for a scoped registry to be set on an existing ShadowRoot. Possibly like:

const registry = new CustomElementRegistry();

class extends HTMLElement {
    #internals = null;
    constructor() {
      super();
      this.#internals = this.attachInternals();
      if (this.#internals.shadowRoot) {
        this.#internals.shadowRoot.registry = registry;
      } else {
        this.attachShadow({mode: 'open', registry});
      }
    }
 }

This means we need to clarify whether the registry can change over time, or whether it can only be set on declarative shadow roots that have been opted out of the global registry. A registry changing over time is something that has also come up in #907 (moving shadow roots between documents).

@rniwa
Copy link
Collaborator

rniwa commented Mar 13, 2021

The proposed solution would mean that a custom element in a declarative shadow DOM can't be upgraded until all its ancestor custom elements had been upgraded. Is that desired behavior in all relevant use cases for declarative shadow DOM whenever scoped custom element registries are involved?

@justinfagnani
Copy link
Contributor Author

This seems fine to me, but maybe I should spell out that implication? I'm not quite how it could be any other way, since imperatively created shadow roots using scoped registries would have been created in top-down order, with each shadow root being created with a scoped registry.

With the "Find a Custom Element Registry" change we have in the explainer:

The registry is found by getting the context node's root. If the root has a CustomElementRegistry, use that registry to look up the definition, otherwise use the global objects CustomElementRegistry object.

we allow for shadow roots that use the global registry to be descendants of shadow roots with a scoped registry. This does allow those nested roots to have their custom elements upgraded. The shadowrootregistry attribute to opt-in to a null registry works for that case.

@calebdwilliams
Copy link

This might be a bad idea—I’m really good at bad ideas—but this feels like a place for a more declarative API (perhaps in addition to the current proposal). If we could name registries and refer to those in the DOM, that could solve for some these issues:

<script>
const registry = new CustomElementRegistry({ name: 'foobar' });

registry.define('x-', class extends HTMLElement {});
</script>
<template shadowroot="open" shadowrootregistry="foobar">
  <x-></x->
</template>

Such that the template will look in some global scope for registries with that name or id and only upgrade from that registry. The obvious downside is that it leaks details about scoped registries which might not be desirable.

@Jamesernator
Copy link

Jamesernator commented Mar 14, 2021

A declarative html registry could be another option e.g.:

<head>
  <registry id="my-element-registry">
    <link rel="customelementdefinition" name="some-element-1" url="path/to/some-element-1.js"/>
    <link rel="customelementdefinition" name="some-element-2" url="path/to/some-element-2.js"/>
  </registry>
</head>

<my-element>
  <not-scoped>content</not-scoped>
  <template shadowroot="open" registry="my-element-registry">
    <some-element-1><slot></slot></some-element-1>
    <some-element-2></some-element-2>
  </template>
</my-element>

@justinfagnani
Copy link
Contributor Author

Given that element definitions require script, I'm not sure there's much to be gained by making registries declarative at this point. Same with registry names. Since you need script already, you can use script to associate registries and shadow roots. When we get to declarative custom elements that may change.

@rniwa
Copy link
Collaborator

rniwa commented Mar 15, 2021

Given that element definitions require script, I'm not sure there's much to be gained by making registries declarative at this point. Same with registry names. Since you need script already, you can use script to associate registries and shadow roots. When we get to declarative custom elements that may change.

The issue is of the order. In the case of a partial hydration, is it true that all element definitions come in top-down order in all use cases? As in, is it really okay that all ancestor custom elements need to be upgraded in order for any descendant custom element to be upgraded?

@Jamesernator
Copy link

Jamesernator commented Mar 16, 2021

Given that element definitions require script, I'm not sure there's much to be gained by making registries declarative at this point. Same with registry names. Since you need script already, you can use script to associate registries and shadow roots. When we get to declarative custom elements that may change.

The issue is of the order. In the case of a partial hydration, is it true that all element definitions come in top-down order in all use cases? As in, is it really okay that all ancestor custom elements need to be upgraded in order for any descendant custom element to be upgraded?

Yeah, the pattern of:

// my-component.js
import Component1 from "component1";
import Component2 from "component2";

const registry = new CustomElementRegistry();

registry.define("component-1", Component1);
registry.define("component-2", Component2);

export default class MyComponent extends HTMLElement {
  #shadowRoot = this.attachShadow({ mode: 'closed', registry });
  
  // ...
}

Means that upgrading will always happen top-down, even if we have something like:

const registry = new CustomElementRegistry();

import("component-1").then(mod => registry.define("component-1", mod.default));
import("component-1").then(mod => registry.define("component-2", mod.default));

export default class MyComponent extends HTMLElement {
  #shadowRoot = this.attachShadow({ mode: "closed", registry });
  
  // ..
}

Upgrading the scoped components is still contingent on when my-component.js finishes fetching and evaluating. If the scoped components managed to fetch earlier (e.g. by <link rel="preloadmodule"), they still can't upgrade those within <template shadowroot="..." customelementregistry=""> as they don't know what their name will be.

Ideally components could opt into as much eager upgrading of subcomponents as they want without being blocked on my-component.js being fetched and evaluated. By doing it in HTML, the browser can trivially upgrade components immediately as it receives them from fetching, regardless of order.

We can't even really hack it properly using inline scripts either, because if want to associate a registry we have to use .attachShadow, which breaks how DSD works (e.g. components need to become aware of this hack, and we'd need to resort to if (this.shadowRoot) checking again rather than the clear and return .attachShadow behaviour).

As such, it really needs to be declared in the HTML somehow, whether by my suggestion of <registry> or even just attributes on the elements themselves (e.g. <my-element customelementdefinition="path/to/my-element.js"></my-element>), or something different entirely. The key point is that as soon as custom elements definitions arrive, we ideally want to be able to upgrade all such components, inside scoped shadow roots, or not.

Some alternative bikesheds for declarative definitions:
<!-- Have elements with set of links and link via <template> attribute -->
<registry id="my-registry">
  <link rel="customelementdefinition" name="component-1" href="./component-1.js">
</registry>

<my-component>
  <template shadowroot="closed" registry="my-registry">
    <component-1></component-1>
  </template>
</my-component>
<!-- Attributes on the to-upgrade elements themselves -->
<my-component>
  <template shadowroot="closed">
    <component-1 customelementdefinition="./component-1.js"></component-1>
  </template>
</my-component>
<!-- Attributes on the template -->
<my-component>
  <template shadowroot="closed" customelement-component-1="./component-1.js">
    <component-1></component-1>
  </template>
</my-component>

@rniwa
Copy link
Collaborator

rniwa commented Mar 16, 2021

Another interaction between declarative shadow DOM & scoped custom element registries. The aforementioned ordering means that adopting scoped custom element registries will change the order by which custom elements are upgraded. Now, the parent custom element needs to be upgraded before their descendent custom elements are upgraded. This might be somewhat surprising consequence of start using a scoped custom element registry and may cause havoc for some websites / web apps with a large number of web components with complex dependencies.

@rniwa
Copy link
Collaborator

rniwa commented Mar 16, 2021

@caridy @JanMiksovsky

@justinfagnani
Copy link
Contributor Author

The issue is of the order. In the case of a partial hydration, is it true that all element definitions come in top-down order in all use cases?

For any chain of nested scoped registry using shadow roots, this is necessarily true. We cannot know what class to use for a name ahead of it.

As in, is it really okay that all ancestor custom elements need to be upgraded in order for any descendant custom element to be upgraded?

"Is it ok" is an interesting question... With imperative only APIs, there's no other practical ordering possible - the parent has to be created before it creates it's own shadow root to have any children to upgrade. With a new way to have deeply nested shadow roots exist before instantiation we may find use cases where we want to upgrade out of order as an optimization. That seems like something that can be pretty transparently added with declarative registries like @Jamesernator has sketched out. We can reserve the space for this in the HTML attribute:

<template shadowroot="open" shadowrootregistry="">

means a null registry that has to be added by script.

<template shadowroot="open" shadowrootregistry="foo">

would utilize some registry-registry system so roots can be associated with registries in arbitrary order.

@caridy
Copy link

caridy commented Mar 16, 2021

The aforementioned ordering issue, if it turns out that it is really needed, it looks to me like it is fatal. Now, I still don't understand the argument from @rniwa very well. As far as I understand, you only need to stop upgrading elements if the nearest declarative shadow has the custom register mark, otherwise you rely on the global registry. In other words:

<my-element>
   <template shadowroot="open" shadowrootregistry="">
          <some-scoped-element>
                <template shadowroot="open">
                        <some-other-element></some-other-element>
                </template>
          </some-scoped-element>
   </template>
</my-element>

The <some-other-element> can be upgraded at any given time, in any given order, no matter what the status of <some-scoped-element> and <my-element> are at the time of the definition of <some-other-element>.

@justinfagnani
Copy link
Contributor Author

The aforementioned ordering issue, if it turns out that it is really needed, it looks to me like it is fatal.

I don't understand this statement. The top-down ordering is exactly what we already have. What's "fatal" about it?

@caridy
Copy link

caridy commented Mar 16, 2021

The aforementioned ordering issue, if it turns out that it is really needed, it looks to me like it is fatal.

I don't understand this statement. The top-down ordering is exactly what we already have. What's "fatal" about it?

@justinfagnani the main thing here is whether or not this new feature changes the existing behavior? (e.g.: order of upgrading)

I, like you, don't think it does it, but I'm not sure that's what @rniwa is saying, so I want to understand more. What he is saying is:

The proposed solution would mean that a custom element in a declarative shadow DOM can't be upgraded until all its ancestor custom elements had been upgraded.

There are two parts of that statement:

  1. It implies a change in the current behavior (today it gets upgraded when the declaration is defined - in a top-down ordering).
  2. It introduces a new narrative "all its ancestor custom elements", which is not relevant today for the upgrade mechanism.

My question to @rniwa is the same, is that statement correct? can you provide an example? I don't think this feature is that disruptive, but as always, I want to understand more about his concern.

If @rniwa concerns turns out to be true, and therefore it requires a change in the current behavior (according to my description above), then I will consider this fatal, and we should get back to the drawing board.

@rniwa
Copy link
Collaborator

rniwa commented Mar 16, 2021

The aforementioned ordering issue, if it turns out that it is really needed, it looks to me like it is fatal.

I don't understand this statement. The top-down ordering is exactly what we already have. What's "fatal" about it?

@justinfagnani the main thing here is whether or not this new feature changes the existing behavior? (e.g.: order of upgrading)

Yeah, sorry I should have been more precise. It doesn't change the behavior of exiting websites.

My question to @rniwa is the same, is that statement correct? can you provide an example? I don't think this feature is that disruptive, but as always, I want to understand more about his concern.

What I mean is that if you have nested custom elements that each use scoped custom element registry and declarative shadow DOM at the same time, then we can only upgrade inner custom elements after outer custom elements had been upgraded regardless of whether they're already available or not in terms of scripts because the browser wouldn't know when which registry will be used for which declarative shadow root.

e.g.

<some-element>
   <template shadowroot="open" customregistry>
          <some-scoped-element>
                <template shadowroot="open" customregistry>
                        <other-scoped-element></other-scoped-element>
                </template>
          </some-scoped-element>
   </template>
</some-element>

Then other-scoped-element can't be upgraded until some-element and some-scoped-element are both defined and upgraded.

This may hinder the ability to adopt scoped custom element registries and declarative shadow DOM in existing apps that use web components because now we're basically forcing the upgrading order to be top-down regardless of whether their respective custom element definitions are ready to go or not. For example, today, you can force bottom-up upgrades in the following tree with just declarative shadow tree if you first define another-element then other-element and some-element at the end, or out-of-order as those scripts arrive.

<some-element>
   <template shadowroot="open">
          <other-element>
                <template shadowroot="open">
                        <another-element></another-element>
                </template>
          </other-element>
   </template>
</some-element>

The inconsistency between when scoped custom element registry is used vs. not used seems problematic / not ergonomic to me.

@caridy
Copy link

caridy commented Mar 16, 2021

@rniwa thanks for the detailed explanation. I do agree with you that there will be some inconsistencies, but that seems ok! Developers will have to choose whether or not they want scoped registry anyways, and if they choose to use it, it is very likely that they have to make significant changes to their app/components to accommodate it while considering the pros and cons.

@justinfagnani I'm good with this proposal. Adding the new attribute on template tag, and adding the setter for ShadowRoot.prototype.registry is good enough IMO. No further questions from my side.

@trusktr
Copy link
Contributor

trusktr commented Mar 22, 2021

This might be somewhat surprising consequence of start using a scoped custom element registry and may cause havoc for some websites / web apps with a large number of web components with complex dependencies.

A web component author today currently has to design their components to work no matter the order that global-registry elements are upgraded.

F.e., an author needs to make sure all of these permutations work:

<script src="parent-el.js"></script>
<script src="child-el.js"></script>
<parent-el><child-el></child-el></parent-el>
<script src="child-el.js"></script>
<script src="parent-el.js"></script>
<parent-el><child-el></child-el></parent-el>
more permutations...
<script src="parent-el.js"></script>
<parent-el><child-el></child-el></parent-el>
<script src="child-el.js"></script>
<script src="child-el.js"></script>
<parent-el><child-el></child-el></parent-el>
<script src="parent-el.js"></script>
<parent-el><child-el></child-el></parent-el>
<script src="parent-el.js"></script>
<script src="child-el.js"></script>
<parent-el><child-el></child-el></parent-el>
<script src="child-el.js"></script>
<script src="parent-el.js"></script>

... etc ...

For any CE author that already designs their elements robustly, the load order differences will not be a problem.

The top-down order is very intuitive and will lead to web developers having an easier time with less upgrade order issues. In React, Vue, Svelte, etc, the order is always top-down. It would confuse people otherwise.

The ease of the developer experience may be something to consider here.

The single most difficult part of Custom Elements (for me) has been dealing with upgrade order (in my case I have elements with custom WebGL rendering instead of CSS, so I need to understand the composed tree shape, and upgrade order has had more impact on that).

@trusktr
Copy link
Contributor

trusktr commented Mar 22, 2021

we need to clarify whether the registry can change over time... A registry changing over time is something that has also come up in #907 (moving shadow roots between documents).

Before that can be possible, #754 needs a solution (and the issue should be left open). If a registry is removed, then

  • the elements in the root are effectively no longer defined relative to the root from which the registry was removed, thus the no-longer-defined elements should be downgraded (I think Option 2 of the "two ideas" is the best way to downgrade).
  • If a new registry is assigned to the root, then the elements in it would be upgraded with their new classes.

@justinfagnani
Copy link
Contributor Author

the elements in the root are effectively no longer defined relative to the root from which the registry was removed, thus the no-longer-defined elements should be downgraded

This does not necessarily follow. It could be decided that if a registry changes that already upgraded elements are left as-is.

There is a tension between trying to put the shadow_root into the state it would have been if it was created with the new registry (no element upgraded from previous registries), and trying to put each individual element into the state it would have been if it was created with the new registry (no orphaned instance properties, event listeners, attributes, shadow children, etc.). I can see arguments either way, as well as one that says because of the tension the registry should not be changeable, but only go from undefined to defined at most once.

@trusktr
Copy link
Contributor

trusktr commented Mar 22, 2021

It could be decided that if a registry changes that already upgraded elements are left as-is.

That's true, that could definitely be decided, but it may lead to unexpected results where a new registry uses the same element name(s) as the previous registry yet the existing elements don't behave according to the new definitions.

only go from undefined to defined at most once.

I think it would be a safe bet to start with this approach, then if/when we want to allow the registry to be changeable it can be added later once it is known what the ideal behavior would be.


Another thing I just thought of is what happens moving an element from one root to another root that has the same element name defined? Would the element be downgraded, then upgraded to use its new class? This would be a rare thing regardless. I've personally never created elements for one root, then moved them to another.

@trusktr
Copy link
Contributor

trusktr commented Mar 22, 2021

orphaned instance properties, event listeners, attributes, shadow children, etc

The CE author would clean up event listeners in disconnectedCallback.

I believe DOM Mutation events could be improved (batched like MutationObserver if not already), and that upgrade and downgrade events could be added. This would make it easy for outside users to also clean up their own event handlers.

Dangling properties and attributes are fine: a user would need to know that they have swapped the definition of an element, and they should set new properties or attributes after the re-upgrade (or leave them if the new definition is compatible, so similar to swapping implementations of an interface, but at runtime).

As for Shadow DOM, that can be handled similar to declarative shadow DOM and ElementInternals: the re-upgraded element will already have a shadow root and the root can be re-used just like in the DSD case. Maybe there would need to be an additional way to detect re-upgrade vs DSD (so to either overwrite the root's content, or re-use the root's content). If not, then perhaps the only sure way is to always overwrite the root's content and never re-use it (or did I miss something?). Well even in the DSD case, how does the author know that the DSD root is own by their framework, and not just some random shadow root some user arbitrarily added into the DOM? Seems like always overwriting content is the safe way to go (but not best for performance due to always re-creating everything).

@Jamesernator
Copy link

Well even in the DSD case, how does the author know that the DSD root is own by their framework, and not just some random shadow root some user arbitrarily added into the DOM? Seems like always overwriting content is the safe way to go (but not best for performance due to always re-creating everything).

This is unknowable, but at the end of day they could as easily mess with your component in a plenty of ways e.g. they could override .attachShadow or just generally modify your components code. If it's served from first-party you might as well just implement under the assumption it's correct, because they can freely break it if they want to and you can't stop them.

Although if your component is loaded as a third-party component (e.g. like a social media button or whatever), then yeah probably best to distrust the shadow root (and still even then, they could just download a copy of the script and modify it, so there's really no winning here).

What I'll probably do is just assume that the component is used correctly, and maybe have a couple basic assertions on things to verify that the wrong thing isn't done and leave it at that.

@Westbrook
Copy link
Collaborator

Question on the use of attachInternals() in the description of this issue. Shadow Roots can be bound to any element, whereas attachInternals() can only be bound to custom elements, IIUC. Does this means that not all shadow roots can have their scoped registries set after the fact?

This may be way late in the process (or maybe just in time 😉), but what if rather than or in addition to applying a scoped registry at attach time:

this.attachShadow({mode: 'open', registry});

We were able to create a shadow root with a scoped registry, much like the shadowrootregistry implies in the description. Creating a shadow root in this way would then include a registry on the shadowRoot that you could register elements on, a la:

import { Button } from './button.js';
import { Input } from './input.js';

// ...

this.attachShadow({mode: 'open', registryType: 'scoped'}); // or some nicer API options
this.shadowRoot.registry.define('my-button', class extends Button {});
this.shadowRoot.registry.define('my-input', class extends Input {});
// ... etc.

If this were the case, we might even be able to recreate the bottom up upgrading missing in:

<some-element>
   <template shadowroot="open" customregistry>
          <some-scoped-element>
                <template shadowroot="open" customregistry>
                        <other-scoped-element></other-scoped-element>
                </template>
          </some-scoped-element>
   </template>
</some-element>

Via JS, like:

const someElement = document.querySelector('some-element');
const someScopedElement = someElement.shadowRoot.querySelector('some-scoped-element');
someScopedElement.shadowRoot.registry.define('other-scoped-element', class extends GenericElement {});

I feel like there's some security something about ElementInternals that this might be avoiding, so thanks for helping clear this up for me! 🙇🏼

@caridy
Copy link

caridy commented Jul 18, 2022

@Westbrook, I think you're right, for something like: <div><template shadowroot="closed" shadowrootregistry=""></template></div>, you will have no way to plug the registry after the fact if div.attachInternals() throws.

@mfreed7 do you recall the details on why it throws for built-ins?

Update: Thinking more about this, the scenario described above might be working fine if we look at this from the perspective of the built-ins. Maybe the shadowrootregistry attribute is just not useful on built-ins, and spec'ing it that way is sufficient.

@Westbrook
Copy link
Collaborator

@caridy does that means that you'd be OK with the following code not being possible:

<div id="shadowroot-div"></div>
<style>
const registry = new CustomElementRegistry();
const div = document.querySelector('#shadowroot-div');
div.attachShadow({mode: 'open', registry});
</style>

Or would built-in's only be hobbled from a Declarative Shadow DOM standpoint?

@mfreed7
Copy link

mfreed7 commented Aug 1, 2022

@mfreed7 do you recall the details on why it throws for built-ins?

Well, ElementInternals is really geared toward custom elements, so you can't attach one for non-CEs. For example, you could otherwise try to get ElementInternals.shadowRoot for an <input> or something like that.

But importantly, I don't think the usage of attachInternals in the OP is really an issue. In that example, it's just being used to more easily grab the shadowRoot for the custom element. If you attach your own shadow root (i.e. outside a CE), you can still use scoped registries:

<div id=host></div>
<script>
const shadow = host.attachShadow({mode: 'open'});
shadow.registry = new CustomElementRegistry();
</script>

Right?

@sashafirsov
Copy link

Scoping registry only within current getRootNode() prevents creation of "horizontal" namespace for libraries of custom elements.
If you familiar with C++ concept of "namespace", it is a natural scoping for related yet distributed across modules( i.e. dom-wide) names.

Root based scoping is a good default for scoping though.
Note, not a shadow root, but general as there is a discussion of components without insulation yet following css scoping+template/slots. I guess getRootNode() is proper anchor for such disregarding shadow root or not.

@rniwa
Copy link
Collaborator

rniwa commented Sep 28, 2022

Sounds like the winning proposal is to add a content attribute to signify that it uses a custom registry as in:

<some-element>
   <template shadowroot="open" customregistry>
          <some-scoped-element>
                <template shadowroot="open" customregistry>
                        <other-scoped-element></other-scoped-element>
                </template>
          </some-scoped-element>
   </template>
</some-element>

We can either automatically create a registry in that case to allow bottom-up upgrades, or force more natural top-down recursive upgrades.

@mfreed7
Copy link

mfreed7 commented Oct 5, 2022

Based on the thumbs-ups on @rniwa's comment above, perhaps this issue is closed/resolved?

@sashafirsov
Copy link

👍🏻 with disclaimer to provide the namespace as value of customregistry attribute. When blank, it would defaults to associated shadow root.

@xiaochengh
Copy link

The "winning proposal" has a (minor?) downside that we can't reuse registry for different shadow roots, even for the same component. So we need to create duplicate registries. For example:

<some-element>
   <template shadowrootmode="open" customregistry>
      <some-scoped-element></some-scoped-element>
      <some-other-scoped-element></some-other-scoped-element>
   </template>
</some-element>
<script type="module">
  class SomeElement extends HTMLElement {
    constructor() {
      super();
      if (this.shadowRoot) {
        // This is duplicated on every instance of SomeElement
        this.shadowRoot.registry.define('some-scoped-element', SomeScopedElement);
        this.shadowRoot.registry.define('some-other-scoped-element', SomeOtherScopedElement);
      } else { ... }
    }
  }
 customElements.define('some-element', SomeElement);
</script>

Not sure how big of an issue it is, since declarative shadow DOM already needs to duplicate the markup and other things. But we can still avoid this by allowing setting ShadowRoot.registry if the ShadowRoot currently has a non-null but empty registry:

<some-element>
   <template shadowrootmode="open" customregistry>
      <some-scoped-element></some-scoped-element>
      <some-other-scoped-element></some-other-scoped-element>
   </template>
</some-element>
<script type="module">
  import { registry } from './some-element-registry.mjs';
  class SomeElement extends HTMLElement {
    constructor() {
      super();
      if (this.shadowRoot) {
        // This makes all SomeElement instances share the same registry
        this.shadowRoot.registry = registry;
      } else { ... }
    }
  }
 customElements.define('some-element', SomeElement);
</script>

I think it has been debated many times at various places whether we should allow setting ShadowRoot.registry, and the general consensus is no, as changing definitions with already upgraded elements can be quite problematic. But if the scoped registry is empty, then I don't see any issue -- it's the same as duplicating the definitions from the other registry. So I don't really see any downside from this proposal, and it's a free performance gain.

@justinfagnani
Copy link
Contributor Author

@xiaochengh

The "winning proposal" has a (minor?) downside that we can't reuse registry for different shadow roots, even for the same component.

But if the scoped registry is empty, then I don't see any issue

This is what I'm proposing here. I think the idea of an attribute is just to note that the shadow root should not use the global registry and await a scoped registry to be set.

@xiaochengh
Copy link

Just discussed this at F2F and resolved:

Present members of WCCG reach consensus on a customregistry attribute, which creates a null registry. Also to add a boolean scopedregistry property on ShadowRoot that is true when in this state.

@keithamus
Copy link
Collaborator

As has already been discussed, WCCG had their spring F2F in which this was discussed. I'd like to point out that you can read the full notes of the discussion (#978 (comment)) in which this was discussed, heading entitled "Scoped Custom Element Registries". As has been pointed out in the comment above, present members of WCCG reached a consensus this issue.

@annevk annevk changed the title [scoped-registries] Interaction with declarative shadow DOM Interaction with declarative shadow DOM Dec 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.