Re: [WICG/webcomponents] Proposal: Custom attributes for all elements, enhancements for more complex use cases (Issue #1029)

LeaVerou left a comment (WICG/webcomponents#1029)

As an update from last month's TC39 class composition discussion, it appears that the existing Stage 1 [first class protocols proposal](https://github.com/tc39/proposal-first-class-protocols) is the way to go for class composition. I plan to work with @michaelficarra to improve its ergonomics so it can cover more use cases and address author and platform needs. @ljharb is also interested in helping move it forwards.

First class protocols provide a nice way to deal with the API issues debated here, as they can be applied to a class at any point, and address the issue around clashes by being symbol-based by default (but there will be a way to say "add string aliases too" for ergonomics, see https://github.com/tc39/proposal-first-class-protocols/issues/47 ). Additionally, they are able to require the host class to fulfill a certain contract (i.e. set certain properties) to get the API provided, which also seems useful here.

Now the challenge is, we don't want to introduce a dependency on first-class protocols that could block custom attributes. So, I think it's fine for the MVP to ship with *no* official way to add API surface (authors can always use authorland conventions), after all the main utility of this is the lifecycle hooks, and those are orthogonal.

---

> I should perhaps make my intent clear about what _I'd_ like to prototype:
> 
> * I'd like to initially prototype a design that is rooted in Custom Elements. 

While the enthusiasm around prototyping is great and I'd hate to get in the way of it, code is generally not a high bandwidth medium for API design brainstorming or collaboration. We should probably have at least _some_ rough consensus about the API shape before said prototyping happens. Since one long GitHub thread is also not particularly conducive I can start an explainer repo and we can collaborate there, if you're ok with that?

> * Essentially whenever a decision needs making, think "what does custom elements do" and do that.

I don't think this is particularly contentious, but as usual, the devil is in the details. It's not always clear what the answer to "what does custom elements do" is. E.g. does `connectedCallback()` fire when the **attribute** is connected, or when the **element** is connected?

> * So in this vein, I'll prototype a `customAttributes` global - just like `customElements`. This would be global and apply to all elements. I don't think binding to specific element sets e.g. `HTMLElement.attributeRegistry.define` would be all that useful, and it would be straightforward to handle this in userland, or do after-the-fact if we found it to be very compelling.

Does that mean it would also be available in SVG elements? Custom elements extend `HTMLElement` explicitly, so it's clear that they are not available in SVG, but if custom attributes don't declare what element interface they apply to anywhere, the expectation should be that they work everywhere. Is that desirable? Is that implementable?
If not, `HTMLElement` should be part of the API, …somewhere.

> * Additionally Custom Attributes would be scopable to ShadowDOM just like Custom Elements are (again, rooted in the same design), this means `attachShadow({ customAttributeRegistry: new CustomAttributeRegistry() })` would allow for the "micro-frontend" style scoping of attribute registries per shadow-dom.

This seems reasonable, but also seems lower priority than the core feature and can probably ship later to keep the initial feature small, just like scoped registries for elements are shipping much later than global ones.

> * I'd like to avoid patching prototypes. I think we could have a "magical" `.custom` property which would be a bit like `.dataset`. So `el.custom.myFoo` would get the `my-foo` attribute node instance. If you wanted to e.g. implement (or polyfill) `my-popover`, you could overwrite patch `HTMLElement.prototype.showPopover()` to forward to `el.custom.myPopover.showPopover()` in your code. Likewise if you want a "reflected attribute" getter/setter that would be for e.g. `get popover() { return this.custom.myPopover.value }`.  Is `el.custom.myPopover.showPopover()` very long? Yes. It's not an API I yearn for but I think it balances the right trade-offs of safety & explicitness without being overly burdensome.
> 

See above.

Also, depending on how far we want to take the `Attr` thing, one could conceivably also hang methods on that. It's not super ergonomic, but in theory if we actually use the `Attr` subclass and not just treat it as a blueprint, authors can add methods to it and they can be accessed via `element.attributes.attrName.method()`. Which doesn't seem that much worse than `element.custom.attrName.method()` (and is more self-explanatory IMO). And authors can always add a single getter on the element itself to make it `element.attrName.method()`.


-- 
Reply to this email directly or view it on GitHub:
https://github.com/WICG/webcomponents/issues/1029#issuecomment-3598001153
You are receiving this because you are subscribed to this thread.

Message ID: <WICG/webcomponents/issues/1029/3598001153@github.com>

Received on Monday, 1 December 2025 17:48:36 UTC