You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Feature detection section
- A couple of other clarifications to mention [Web Platform Design Principles](https://www.w3.org/TR/design-principles/) more explicitly
- Update Open questions section: first question (automatic exposure of properties in the element) has been compacted and second question (dynamic behaviors) has been deleted.
Platform behaviors give custom elements capabilities that would otherwise require reimplementation or workarounds. Each behavior automatically provides:
103
103
104
-
- Event handling: Platform events (click, keydown, etc.) are wired up automatically.
104
+
- Event handling: Platform events (click, keydown, etc.) are wired up automatically using the standard DOM event infrastructure (respecting `stopPropagation`, `preventDefault`, etc.)
105
105
- ARIA defaults: Implicit roles and properties for accessibility.
106
106
- Focusability: The element participates in the tab order as appropriate for the behavior.
107
107
- CSS pseudo-classes: Behavior-specific pseudo-classes are managed by the platform.
108
108
109
+
By bundling these capabilities as high-level units, the platform can ensure accessible defaults, correct event wiring, and proper pseudo-class management.
110
+
109
111
This proposal introduces `HTMLSubmitButtonBehavior`, which mirrors the submission capability of `<button type="submit">`:
110
112
111
113
| Capability | Details |
@@ -196,7 +198,7 @@ When `attachInternals()` is called with behaviors, each behavior is attached to
196
198
| Element disconnected from DOM | Behavior state is preserved. Event handlers remain conceptually attached but inactive. |
197
199
| Element reconnected to DOM | Event handlers become active again. Behavior state (e.g., `formAction`, `disabled`) is preserved. |
198
200
199
-
*Note: Behaviors are immutable after `attachInternals()`. See the [open question on dynamic behaviors](#should-we-support-dynamic-behavior-updates).*
201
+
*Note: Behaviors are immutable after `attachInternals()`. Dynamic behavior updates (adding, removing, or replacing behaviors after attachment) are not supported, as developer feedback indicated that the problems with `<input>`'s mutable `type` attribute (state migration, event handler cleanup, property compatibility) should not be replicated.*
200
202
201
203
### Duplicate behaviors
202
204
@@ -233,10 +235,10 @@ This ensures that element-specific properties like `behavior.form` and `behavior
233
235
The current API uses instantiated behaviors with a single `behaviors` property:
- `behaviors` property on `ElementInternals` is a read-only array.
238
+
- `behaviors` property on `ElementInternals` is a read-only `FrozenArray`.
237
239
- Developers hold direct references to their behavior instances.
238
240
239
-
*Note: An array is preferred over a set because order may be significant for [conflict resolution](#behavior-composition-and-conflict-resolution). A set provides no ordering guarantees, which would make conflict resolution unpredictable.*
241
+
*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.*
- Less setup code as developers don't manage behavior instances.
286
288
287
289
**Cons:**
288
-
- Platform instantiates the behavior, so constructor parameters aren't available.
290
+
- 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.
289
291
- Requires a `behaviors` interface for named access.
290
292
- *Future* developer-defined behaviors would need a way to name their behaviors.
291
293
@@ -494,6 +496,24 @@ class LabeledSubmitButton extends HTMLElement {
494
496
- Adds complexity for simple cases where order-based resolution would suffice.
495
497
- Authors must understand all potential conflicts to resolve them correctly.
496
498
499
+
### Feature detection
500
+
501
+
Web authors can detect whether behaviors are supported by checking for the existence of behavior classes on the global scope:
502
+
503
+
```javascript
504
+
if (typeof HTMLSubmitButtonBehavior !=='undefined') {
This proposal supports common web component patterns:
@@ -821,108 +841,14 @@ Although this proposal currently focuses on custom elements, the behavior patter
821
841
822
842
### Should behavior properties be automatically exposed on the element?
823
843
824
-
The current proposal requires developers to manually create getters/setters that delegate to the stored behavior instance (e.g., `this._submitBehavior.*`). There are alternative approaches worth considering:
- Authors have full control over their element's public API.
830
-
- No naming conflicts.
831
-
- Authors can add validation, transformation, or side effects in setters.
832
-
- Familiar pattern.
833
-
834
-
**Cons:**
835
-
- Boilerplate for each property the author wants to expose.
836
-
- For future behaviors like `HTMLInputBehavior`, not exposing `value` means external code can't read or set the input's data without the developer writing boilerplate getters and setters.
837
-
838
-
#### Option B: Automatic property exposure
839
-
840
-
The platform automatically adds behavior properties to the custom element:
- Zero boilerplate code to get and set properties.
859
-
- Matches how native elements work (a `<button>` just has `disabled`).
860
-
- For future behaviors like `HTMLCheckboxBehavior` external code can read/write `checked` without the developer writing any delegation code.
861
-
862
-
**Cons:**
863
-
- Naming conflicts if the element already defines a property with the same name.
864
-
- Less control over the public API surface.
865
-
- Authors can't easily add validation or side effects to setters.
866
-
- May feel "magical" compared to explicit delegation.
867
-
- Unclear behavior when a behavior is removed. If `formAction` was automatically exposed when `HTMLSubmitButtonBehavior` was attached, what happens to that property after the behavior is removed? Does it remain on the element with a stale value, or is it removed?
exposeProperties:true// or list specific properties.
890
-
});
891
-
```
892
-
893
-
**Pros:**
894
-
- Flexibility: authors choose the right approach for their use case.
895
-
- Backwards compatible with explicit delegation.
896
-
897
-
**Cons:**
898
-
- More complex API.
899
-
- Still needs to handle naming conflicts when `exposeProperties` is enabled.
900
-
901
-
#### Why this matters
902
-
903
-
Future behaviors would likely require developers to expose certain properties for the element to be useful to consumers. Without developer-written delegation, external JavaScript code can't access these properties:
904
-
905
-
| Behavior | Key property | Impact if not exposed |
906
-
|----------|------------------|------|
907
-
| `HTMLCheckboxBehavior` | `checked` | The behavior toggles internal state on click, but external scripts can't read or set it. |
908
-
| `HTMLInputBehavior` | `value` | External scripts can't programmatically read the input's data or populate it. (Form submission would still work via `setFormValue()`.) |
909
-
| `HTMLRadioGroupBehavior` | `checked` | Mutual exclusion happens internally, but external scripts can't query which radio is selected. |
910
-
911
-
If `HTMLSubmitButtonBehavior` uses manual delegation but `HTMLCheckboxBehavior` uses automatic exposure, we'd have an inconsistent API surface. This argues for deciding on a consistent approach across all behaviors from the start.
912
-
913
-
### Should we support dynamic behavior updates?
914
-
915
-
This proposal uses static behaviors: once attached via `attachInternals()`, behaviors cannot be added, removed, or replaced. One argument to support dynamic behavior updates is to mirror native `<input>` element flexibility, where changing the `type` attribute switches between radically different behaviors (text field → checkbox → date picker). However, feedback suggests that `<input>`'s design shouldn't be emulated:
916
-
917
-
- The `type` attribute fundamentally changes what the element is.
918
-
- Different input types have incompatible properties (`checked` vs `value` vs `files`).
919
-
- The design makes `<input>` difficult to style and reason about.
844
+
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.
920
845
921
-
*See [Monica Dinculescu's analysis](https://meowni.ca/posts/a-story-about-input/) documenting the problems with `<input>`.*
846
+
Two alternatives have been considered:
922
847
923
-
For behaviors, the same problems would apply: if behaviors could be swapped dynamically, authors would need to handle state migration, event handler cleanup, and property compatibility.
848
+
- **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."
849
+
- **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.
924
850
925
-
If compelling use cases emerge that genuinely require dynamic behavior composition, the API could be extended to use `ObservableArray` with lifecycle callbacks. This would be a backwards-compatible change (making the array mutable doesn't break code that treats it as read-only). However, we believe static behaviors will cover the vast majority of real-world needs.
851
+
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.
926
852
927
853
### Is there a better name than "behavior" for this concept?
- Primitives like `disabled` and `focusable` interact with each other, with accessibility, and with event handling. Setting `internals.disabled=true` without the associated behavior might result in the element *looking* disabled but still receiving clicks, remaining in the tab order, and submitting with a form.
1092
-
- Even seemingly simple primitives like focusability could have significant complexity around accessibility integration. This is why `popovertarget` is limited to buttons(it was originally intended for any element, but the accessibility requirements around focusability and activation made buttons the practical choice).
1018
+
- Even seemingly simple primitives like focusability could have significant complexity around accessibility integration. This is why `popovertarget` is limited to buttons(it was originally intended for any element, but the accessibility requirements around focusability and activation made buttons the practical choice). See [design-principles tradeoff between high-level and low-level APIs](https://www.w3.org/TR/design-principles/#high-level-low-level).
1093
1019
- Form submission participation can be seen as a primitive itself (it can't be broken down further due to accessibility concerns).
0 commit comments