From f11117220dda53179d9e9ee3604caca08efc74df Mon Sep 17 00:00:00 2001 From: Ana Sollano Kim Date: Wed, 1 Apr 2026 16:20:13 -0700 Subject: [PATCH 1/5] Open questions changes --- PlatformProvidedBehaviors/explainer.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/PlatformProvidedBehaviors/explainer.md b/PlatformProvidedBehaviors/explainer.md index 82dff15a..a68ef45d 100644 --- a/PlatformProvidedBehaviors/explainer.md +++ b/PlatformProvidedBehaviors/explainer.md @@ -172,6 +172,8 @@ class CustomSubmitButton extends HTMLElement { To expose properties like `disabled` or `formAction` to external code, authors define getters and setters that delegate to the behavior. This gives authors full control over their element's public API. +*Why not expose behavior properties automatically on the element?* Automatic exposure would require adding behavior properties (e.g., `formAction`, `name`, `value`) to `HTMLElement`'s IDL statically, since IDL attributes can't be added dynamically at runtime. This would bloat every `HTMLElement` instance's prototype — including `
`, ``, etc. — with properties that only make sense for elements with specific behaviors. It would also introduce naming conflicts with author-defined properties and remove API control. An opt-in variant (e.g., `exposeProperties: true`) was also considered but adds API complexity without sufficient benefit. Manual delegation is more boilerplate but avoids these problems. + Authors are also responsible for attribute reflection. If the author wants HTML attributes on their custom element (e.g., ``) to affect the behavior, they need to observe and forward those attributes using `attributeChangedCallback`: ```javascript @@ -839,17 +841,6 @@ Although this proposal currently focuses on custom elements, the behavior patter ## Open questions -### Should behavior properties be automatically exposed on the element? - -The current proposal uses manual property delegation: developers create getters/setters that delegate to the stored behavior instance. This gives authors full control over their public API, avoids naming conflicts, and allows validation or side effects in setters. The tradeoff is boilerplate for each exposed property. - -Two alternatives have been considered: - -- **Automatic property exposure:** The platform adds behavior properties directly to the custom element (e.g., `btn.disabled = true` works without any getter/setter). This matches how native elements work but introduces naming conflicts, reduces API control, and feels "magical." -- **Opt-in automatic exposure:** Authors choose per-element whether properties are auto-exposed via an option like `exposeProperties: true`. This offers flexibility but adds API complexity. - -Future behaviors like `HTMLCheckboxBehavior` or `HTMLInputBehavior` would require developers to write boilerplate for key properties (`checked`, `value`) that external code needs to access. A consistent approach across all behaviors should be decided before additional behaviors ship. - ### Is there a better name than "behavior" for this concept? The American English spelling of behavior throughout this proposal follows the [WHATWG spec style guidelines](https://wiki.whatwg.org/wiki/Specs/style#:~:text=Use%20standard%20American%20English%20spelling). However, the word "behavior" has some drawbacks: From e0557b3fe4deb480b5f191ebe19ef46c59154bfb Mon Sep 17 00:00:00 2001 From: Ana Sollano Kim Date: Wed, 1 Apr 2026 16:25:34 -0700 Subject: [PATCH 2/5] Nit --- PlatformProvidedBehaviors/explainer.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PlatformProvidedBehaviors/explainer.md b/PlatformProvidedBehaviors/explainer.md index a68ef45d..ea268884 100644 --- a/PlatformProvidedBehaviors/explainer.md +++ b/PlatformProvidedBehaviors/explainer.md @@ -172,8 +172,6 @@ class CustomSubmitButton extends HTMLElement { To expose properties like `disabled` or `formAction` to external code, authors define getters and setters that delegate to the behavior. This gives authors full control over their element's public API. -*Why not expose behavior properties automatically on the element?* Automatic exposure would require adding behavior properties (e.g., `formAction`, `name`, `value`) to `HTMLElement`'s IDL statically, since IDL attributes can't be added dynamically at runtime. This would bloat every `HTMLElement` instance's prototype — including `
`, ``, etc. — with properties that only make sense for elements with specific behaviors. It would also introduce naming conflicts with author-defined properties and remove API control. An opt-in variant (e.g., `exposeProperties: true`) was also considered but adds API complexity without sufficient benefit. Manual delegation is more boilerplate but avoids these problems. - Authors are also responsible for attribute reflection. If the author wants HTML attributes on their custom element (e.g., ``) to affect the behavior, they need to observe and forward those attributes using `attributeChangedCallback`: ```javascript @@ -190,6 +188,8 @@ class CustomButton extends HTMLElement { } ``` +*Note: Automatic exposure would require adding behavior properties (e.g., `formAction`, `name`, `value`) to `HTMLElement`. This would bloat every `HTMLElement` instance's prototype with properties that only make sense for elements with specific behaviors. An opt-in alternative was considered but adds API complexity without benefit.* + ### Behavior lifecycle When `attachInternals()` is called with behaviors, each behavior is attached to the element: From 809c8eceb36b08f5dec90e830fe0ebcc6a3569c8 Mon Sep 17 00:00:00 2001 From: Ana Sollano Kim Date: Thu, 2 Apr 2026 10:40:00 -0700 Subject: [PATCH 3/5] Resolve api design prelim --- PlatformProvidedBehaviors/explainer.md | 33 +++----------------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/PlatformProvidedBehaviors/explainer.md b/PlatformProvidedBehaviors/explainer.md index ea268884..82a1b674 100644 --- a/PlatformProvidedBehaviors/explainer.md +++ b/PlatformProvidedBehaviors/explainer.md @@ -234,7 +234,7 @@ This ensures that element-specific properties like `behavior.form` and `behavior ### API design -The current API uses instantiated behaviors with a single `behaviors` property: +Behaviors are instantiated with `new` and passed to `attachInternals()`: - `behaviors` option in `attachInternals({ behaviors: [...] })` accepts behavior instances. - `behaviors` property on `ElementInternals` is a read-only `FrozenArray`. @@ -242,15 +242,9 @@ The current API uses instantiated behaviors with a single `behaviors` property: *Note: An ordered array is preferred over a set because order may be significant for [conflict resolution](#behavior-composition-and-conflict-resolution). `behaviors` uses a `FrozenArray` because behaviors are immutable after attachment.* -**Pros:** -- Single property name. -- No array lookup or `instanceof` checks needed as developers hold direct references. -- *Future* developer-defined behaviors are simpler: just instantiate and attach. -- Consistent mental model: behaviors are objects you create and manage. +This design was chosen over an alternative where class references (rather than instances) are passed to `attachInternals()` and the platform instantiates the behaviors. With the current approach, developers hold direct references to their behavior instances — no array lookup, `instanceof` checks, or naming scheme is needed to access behavior state. It also aligns with the [W3C design principle that classes should have constructors](https://www.w3.org/TR/design-principles/#constructors) that allow authors to create and configure instances, and it extends naturally to future developer-defined behaviors that follow the same `new` + attach pattern. -**Cons:** -- Requires developers to manage behavior instances themselves. -- More setup code compared to passing class references directly. +The behavior instance is inert before attachment: no event handlers fire and no form association exists. The window between `new HTMLSubmitButtonBehavior()` and `attachInternals()` is typically one or two lines inside the constructor, so there is no meaningful intermediate state for developers to manage. Keeping creation separate from attachment avoids a circular dependency (where `attachInternals()` needs the behavior instances but the behaviors would need the internals first) and enables useful patterns like choosing which behavior to instantiate based on element attributes. *For future developer-defined behaviors:* @@ -272,27 +266,6 @@ this._internals = this.attachInternals({ behaviors: [this._tooltipBehavior] }); this._tooltipBehavior.content = 'Helpful tooltip text'; ``` -#### Alternative 1: Class references - -Pass behavior classes (not instances) to `attachInternals()`: - -```javascript -// Attach a behavior during initialization (class reference). -this._internals = this.attachInternals({ behaviors: [HTMLSubmitButtonBehavior] }); - -// Access behavior state via named accessor. -this._internals.behaviors.htmlSubmitButton.formAction = '/custom'; -``` - -**Pros:** -- Named access via `this._internals.behaviors.` requires no iteration. -- Less setup code as developers don't manage behavior instances. - -**Cons:** -- Platform instantiates the behavior, so constructor parameters aren't available. This conflicts with the [design principle that classes should have constructors](https://www.w3.org/TR/design-principles/#constructors) that allow authors to create and configure instances. -- Requires a `behaviors` interface for named access. -- *Future* developer-defined behaviors would need a way to name their behaviors. - ### Behavior composition and conflict resolution When multiple behaviors are attached to an element, they may provide overlapping capabilities. This section discusses strategies for resolving such conflicts. From 42dbabec7136ad59cd66820222322955afd40416 Mon Sep 17 00:00:00 2001 From: Ana Sollano Kim Date: Thu, 2 Apr 2026 10:56:21 -0700 Subject: [PATCH 4/5] Revision --- PlatformProvidedBehaviors/explainer.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/PlatformProvidedBehaviors/explainer.md b/PlatformProvidedBehaviors/explainer.md index 82a1b674..1cc6f90e 100644 --- a/PlatformProvidedBehaviors/explainer.md +++ b/PlatformProvidedBehaviors/explainer.md @@ -188,7 +188,7 @@ class CustomButton extends HTMLElement { } ``` -*Note: Automatic exposure would require adding behavior properties (e.g., `formAction`, `name`, `value`) to `HTMLElement`. This would bloat every `HTMLElement` instance's prototype with properties that only make sense for elements with specific behaviors. An opt-in alternative was considered but adds API complexity without benefit.* +*Note: Automatic exposure would require adding behavior properties (e.g., `formAction`, `name`, `value`) to `HTMLElement`. This would bloat every `HTMLElement` instance's prototype with properties that only make sense for elements with specific behaviors. An opt-in alternative was considered but adds API complexity without a clear benefit.* ### Behavior lifecycle @@ -242,9 +242,7 @@ Behaviors are instantiated with `new` and passed to `attachInternals()`: *Note: An ordered array is preferred over a set because order may be significant for [conflict resolution](#behavior-composition-and-conflict-resolution). `behaviors` uses a `FrozenArray` because behaviors are immutable after attachment.* -This design was chosen over an alternative where class references (rather than instances) are passed to `attachInternals()` and the platform instantiates the behaviors. With the current approach, developers hold direct references to their behavior instances — no array lookup, `instanceof` checks, or naming scheme is needed to access behavior state. It also aligns with the [W3C design principle that classes should have constructors](https://www.w3.org/TR/design-principles/#constructors) that allow authors to create and configure instances, and it extends naturally to future developer-defined behaviors that follow the same `new` + attach pattern. - -The behavior instance is inert before attachment: no event handlers fire and no form association exists. The window between `new HTMLSubmitButtonBehavior()` and `attachInternals()` is typically one or two lines inside the constructor, so there is no meaningful intermediate state for developers to manage. Keeping creation separate from attachment avoids a circular dependency (where `attachInternals()` needs the behavior instances but the behaviors would need the internals first) and enables useful patterns like choosing which behavior to instantiate based on element attributes. +With the current approach, developers hold direct references to their behavior instances: no array lookup, `instanceof` checks, or `behaviors` interface is needed to access behavior state. It also aligns with the [W3C design principle that classes should have constructors](https://www.w3.org/TR/design-principles/#constructors) that allow authors to create and configure instances, and it extends naturally to future developer-defined behaviors that follow the same `new` + attach pattern. *For future developer-defined behaviors:* From c4e79fa8bceeaf8350e88c2f7278d36de7e422ad Mon Sep 17 00:00:00 2001 From: Ana Sollano Kim Date: Mon, 6 Apr 2026 10:21:09 -0700 Subject: [PATCH 5/5] Addressing feedback I --- PlatformProvidedBehaviors/explainer.md | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/PlatformProvidedBehaviors/explainer.md b/PlatformProvidedBehaviors/explainer.md index 1cc6f90e..7cb620b7 100644 --- a/PlatformProvidedBehaviors/explainer.md +++ b/PlatformProvidedBehaviors/explainer.md @@ -188,8 +188,6 @@ class CustomButton extends HTMLElement { } ``` -*Note: Automatic exposure would require adding behavior properties (e.g., `formAction`, `name`, `value`) to `HTMLElement`. This would bloat every `HTMLElement` instance's prototype with properties that only make sense for elements with specific behaviors. An opt-in alternative was considered but adds API complexity without a clear benefit.* - ### Behavior lifecycle When `attachInternals()` is called with behaviors, each behavior is attached to the element: @@ -242,7 +240,7 @@ Behaviors are instantiated with `new` and passed to `attachInternals()`: *Note: An ordered array is preferred over a set because order may be significant for [conflict resolution](#behavior-composition-and-conflict-resolution). `behaviors` uses a `FrozenArray` because behaviors are immutable after attachment.* -With the current approach, developers hold direct references to their behavior instances: no array lookup, `instanceof` checks, or `behaviors` interface is needed to access behavior state. It also aligns with the [W3C design principle that classes should have constructors](https://www.w3.org/TR/design-principles/#constructors) that allow authors to create and configure instances, and it extends naturally to future developer-defined behaviors that follow the same `new` + attach pattern. +Developers hold direct references to their behavior instances. This aligns with the [W3C design principle that classes should have constructors](https://www.w3.org/TR/design-principles/#constructors) that allow authors to create and configure instances, and it extends naturally to future developer-defined behaviors that follow the same `new` + attach pattern. *For future developer-defined behaviors:* @@ -1004,6 +1002,27 @@ customElements.define('custom-button', CustomButton); `HTMLSubmitButtonBehavior` could itself be designed as a decorator, but decorators can't easily access `ElementInternals` or instance state during application. Decorators would need to coordinate with `attachInternals()` timing. Additionally, getting a reference to the behavior instance for property access (e.g., `behavior.formAction`) would require additional wiring. +### Alternative to API design: Class references + +Pass behavior classes (not instances) to `attachInternals()`: + +```javascript +// Attach a behavior during initialization (class reference). +this._internals = this.attachInternals({ behaviors: [HTMLSubmitButtonBehavior] }); + +// Access behavior state via named accessor. +this._internals.behaviors.htmlSubmitButton.formAction = '/custom'; +``` + +**Pros:** +- Named access via `this._internals.behaviors.` requires no iteration. +- Less setup code as developers don't manage behavior instances. + +**Cons:** +- Platform instantiates the behavior, so constructor parameters aren't available. This conflicts with the [design principle that classes should have constructors](https://www.w3.org/TR/design-principles/#constructors) that allow authors to create and configure instances. +- Requires a `behaviors` interface for named access. +- *Future* developer-defined behaviors would need a way to name their behaviors. + ## Accessibility, security, and privacy considerations ### Accessibility