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
* move buttonType to Future work
* remove interestfor
* add elementInternals.role
* update stakeholder feedback
* add comparison
* fix typo
Co-authored-by: Dan Clark <daniec@microsoft.com>
* update comparision examples
* update comparison example
* remove redundant ```
* revise comparison example
* update buttonType with "button" as default value
* remove the element appearance section as it's more of a section for the behavesLike approach, not the main proposal.
* update Comparison
* fix typo
---------
Co-authored-by: Dan Clark <daniec@microsoft.com>
@@ -15,7 +15,7 @@ Web component authors often want to create custom elements that have the activat
15
15
16
16
- Custom buttons can be [popover invokers](https://html.spec.whatwg.org/multipage/popover.html#popoverinvokerelement) while providing unique styles and additional functionality (as discussed [here](https://github.com/openui/open-ui/issues/1088)).
17
17
18
-
- Custom buttons can provide native [submit button](https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type-submit) behavior so that the custom button can implicitly [submit forms](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-form-submit). Similarly, custom buttons can also provide native [reset button](https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type-reset) behavior that can implicitly [reset forms](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-form-reset)(as discussed [here](https://github.com/WICG/webcomponents/issues/814)).
18
+
- Custom buttons can provide native [submit button](https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type-submit) behavior so that the custom button can implicitly [submit forms](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-form-submit) (as discussed [here](https://github.com/WICG/webcomponents/issues/814)). Similarly, custom buttons can also provide native [reset button](https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type-reset) behavior that can [reset forms](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-form-reset) .
19
19
20
20
Currently, web developers face challenges when trying to implement these behaviors in custom elements. The existing customized built-in approach using `extends` and `is` provides native button functionality but lacks full cross-browser support. As a result, developers are forced to manually reimplement button behaviors from scratch, leading to inconsistent implementations, accessibility issues, and development overhead.
21
21
@@ -25,12 +25,12 @@ This proposal addresses these challenges by introducing a standardized way for c
25
25
- A solution to support key button activation use cases, particularly command invocation and form submission
26
26
27
27
### Non-goals
28
-
- Providing a comprehensive alternative to the customized built-in solution (`extends` and `is`), i.e., enabling a custom element to do everything a native button does.
28
+
- Providing an alternative to the customized built-in solution (`extends` and `is`), i.e., enabling a custom element to do everything a native button does.
29
29
- A declarative version of this proposal. This requires finding a general solution for declarative custom elements, which should be explored separately.
We propose enabling web component authors to create custom elements with button activation behaviors by adding a static `buttonActivationBehaviors` property to their custom element class definition.
33
-
This proposal focuses on decomposing native element behaviors into specific functionalities that can be individually exposed through `ElementInternals`. This approach builds on the existing pattern established by form-associated custom elements ([FACEs](https://html.spec.whatwg.org/dev/custom-elements.html#form-associated-custom-elements)) and accessibility semantics ([ARIAMixin](https://www.w3.org/TR/wai-aria-1.2/#ARIAMixin)), where specific capabilities are exposed as discrete APIs that web developers can combine as needed.
33
+
This approach builds on the existing pattern established by form-associated custom elements ([FACEs](https://html.spec.whatwg.org/dev/custom-elements.html#form-associated-custom-elements)) and accessibility semantics ([ARIAMixin](https://www.w3.org/TR/wai-aria-1.2/#ARIAMixin)), where specific capabilities are exposed as discrete APIs that web developers can combine as needed.
**Note**: Future work may include adding support for the `interestfor` attribute, which is currently [shipping in Chromium](https://chromestatus.com/feature/4530756656562176?gate=4768466822496256) as an experimental feature for interest-based element targeting.
67
-
68
66
**Implicit behaviors:**
69
67
Beyond attributes, properties, and events, custom elements with `buttonActivationBehaviors = true` also gain native behaviors related to button activation:
70
68
-**Click event activation**: The element fires click events when activated via mouse click, Enter key, Space key, or other activation methods
@@ -74,9 +72,6 @@ Beyond attributes, properties, and events, custom elements with `buttonActivatio
74
72
### Order of precedence regarding ARIA role
75
73
The order is `<custom-button role=foo>` > `ElementInternals.role` > default `button` role via `buttonActivationBehaviors`
76
74
77
-
### `buttonActivationBehaviors` does not change element appearance
78
-
Setting `buttonActivationBehaviors` gives a custom element button activation behaviors, but the custom element's appearance does not change. In other words, the custom element does not take on default, author-specified or user-specified styles that target the native button element, since the custom element has a different tag name (e.g., `<fancy-button>` instead of `<button>`).
79
-
80
75
## Examples
81
76
### Custom button with popover invocation
82
77
This example shows how to create a custom button that can invoke a popover element using the `commandfor` and `command` attributes:
The following examples demonstrate how much JS code can be saved with this proposal when a custom element author wants to support `command`/`commandfor` attributes:
141
+
### Without `buttonActivationBehaviors`
142
+
```js
143
+
classCustomButtonextendsHTMLElement {
144
+
constructor() {
145
+
super();
146
+
this.internals_=this.attachInternals();
147
+
}
148
+
149
+
connectedCallback() {
150
+
// ARIA role assignment
151
+
this.internals_.role='button';
144
152
145
-
## Add `buttonType` property in `ElementInternals`
146
-
To provide submit and reset functionality, this proposal also introduces a `buttonType` property to `ElementInternals` that controls the behavior when the custom element is activated.
153
+
// Make element focusable
154
+
if (!this.hasAttribute('tabindex')) {
155
+
this.tabIndex=0;
156
+
}
147
157
148
-
The `ElementInternals` interface would be extended with:
149
-
- `buttonType` - controls the activation behavior of the button (values: "button", "submit", "reset")
158
+
this.addEventListener('click', this.handleClick);
159
+
}
160
+
161
+
handleClick() {
162
+
consttargetId=this.getAttribute('commandfor');
163
+
constcommand=this.getAttribute('command');
164
+
consttarget=document.getElementById(targetId);
165
+
166
+
if (target) {
167
+
if (command ==="toggle-popover") {
168
+
target.togglePopover();
169
+
} elseif (command ==="show-popover") {
170
+
target.showPopover();
171
+
} elseif (command ==="hide-popover") {
172
+
target.hidePopover();
173
+
} elseif (command ==="show-modal") {
174
+
target.showModal()
175
+
} elseif (command ==="close") {
176
+
target.close()
177
+
} elseif (command ==="request-close") {
178
+
target.requestClose()
179
+
}
180
+
181
+
// Create and fire command event at the target element
### Add `buttonType` property on `ElementInternals`
203
+
To support _submit_ and _reset_ functionality, this proposal can be extended with a new `buttonType` property on `ElementInternals`, which defines how the custom element behaves when activated:
204
+
205
+
- `"button"` - (Default) No special form behavior, only fires click events and command invocation
206
+
- `"submit"` - Submits the associated form when activated
207
+
- `"reset"` - Resets the associated form when activated
150
208
151
209
If `buttonType` is set to any other value, it will fall back to the default value.
152
210
153
211
**IDL definitions:**
154
212
```webidl
155
213
partial interface ElementInternals {
156
-
[CEReactions] attribute DOMString buttonType;
214
+
attribute DOMString buttonType;
157
215
};
158
216
```
159
217
160
-
**Activation behaviors:**
161
-
- `"button"` - No special form behavior, only fires click events and command invocation
162
-
- `"submit"` - Submits the associated form when activated
163
-
- `"reset"` - Resets the associated form when activated
164
-
165
-
Following the [HTML specification for button's type attribute](https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type), the default `buttonType` is `"submit"` unless either the `command` or `commandfor` content attributes are present, in which case the default is `"button"`.
166
-
167
-
### `buttonType` and `formAssociated`
218
+
#### `buttonType` and `formAssociated`
168
219
When `buttonActivationBehaviors` is set to true, the custom element's form association behavior depends on the `buttonType` value:
169
220
170
221
- **`buttonType ="submit"` or `"reset"`**: The element automatically becomes form-associated and `static formAssociated =true/false;` becomes a no-op.
@@ -195,7 +246,7 @@ If any of these properties are accessed on a custom element that does not have b
195
246
**Implicit behaviors:**
196
247
- **Form submission**: When associated with a `<form>`, pressing Enter on an associated form control (e.g., a text input) will trigger submit behavior if the custom element `buttonType` is `"submit"`.
If additional behaviors are introduced in the future, how should potential conflicts be addressed? For example:
286
+
287
+
- **Conflicting semantics**: How should we handle ambiguity when combining command invocation behavior with label behavior? Should the element have an ARIA role of `button`, or none at all since labels lack an implicit ARIA role?
288
+
- **Interaction conflicts**: When clicked, the [Invoker Commands API](https://developer.mozilla.org/en-US/docs/Web/API/Invoker_Commands_API) will automatically trigger command invocation, but should the element also transfer focus to a labeled control (label behavior)? Would this dual behavior create confusion or negatively impact the user experience?
289
+
- **Specification complexity**: How can we define conflict resolution for these combinations without introducing excessive edge cases and complexity?
290
+
291
+
```js
292
+
classCustomElementextendsHTMLElement {
293
+
static buttonActivationBehaviors =true;
294
+
static labelBehaviors =true;
295
+
296
+
constructor() {
297
+
super();
298
+
this.internals_=this.attachInternals();
299
+
}
300
+
301
+
getcommandForElement() {
302
+
returnthis.internals_.commandForElement??null;
303
+
}
304
+
305
+
setcommandForElement(element) {
306
+
this.internals_.commandForElement= element;
307
+
}
308
+
309
+
getcontrol() {
310
+
returnthis.internals_.control??null;
311
+
}
312
+
313
+
sethtmlFor(value) {
314
+
this.internals_.htmlFor= value;
315
+
}
316
+
317
+
// Manual handling of label behavior (command invocation is handled automatically)
318
+
handleLabelClick(event) {
319
+
// Should this also transfer focus to labeled control (label behavior)?
320
+
// Developers must resolve this conflict manually since command invocation
321
+
// is automatically handled by the Invoker Commands API
322
+
if (this.control) {
323
+
// Label behavior: focus the labeled control
324
+
this.control.focus();
325
+
}
326
+
}
327
+
328
+
connectedCallback() {
329
+
// Manual role conflict resolution - developers must decide
330
+
// whether this should be a button or label
331
+
this.internals_.role='button'; // or no role for label behavior?
332
+
333
+
// Manual focus management for button behavior
334
+
if (!this.hasAttribute('tabindex')) {
335
+
this.tabIndex=0;
336
+
}
233
337
338
+
// Manual event handling for label behavior (command invocation is automatic)
@@ -317,7 +427,7 @@ The `ElementInternals` Interface would be extended with minimal command invocati
317
427
**Trade-offs of this approach**
318
428
- **Accessibility risks**: Without automatic defaults, developers may forget to implement critical accessibility features, leading to inaccessible custom elements.
319
429
320
-
We consulted the [ARIA Working Group](https://github.com/w3c/aria/issues/2637) on this approach versus the main proposal with built-in defaults (implicit behaviors), and the [overwhelming consensus](https://www.w3.org/2025/09/25-aria-minutes.html#d0af) was to provide accessibility defaults that can be potentially overwritten by "power users" (main proposal).
430
+
We consulted the [ARIA Working Group](https://github.com/w3c/aria/issues/2637) on this approach versus the main proposal with built-in defaults (implicit behaviors), and the [overwhelming consensus](https://www.w3.org/2025/09/25-aria-minutes.html#d0af) was to provide accessibility defaults that can be potentially overwritten by "power users" through `elementInternals.role`.
321
431
322
432
### 2. Static `behavesLike` property with behavior-specific interface mixins
323
433
This alternative approach enables web component authors to create custom elements with native behaviors by adding a static `behavesLike` property to their custom element class definition. This property can be set to string values that represent native element types:
@@ -538,73 +648,11 @@ A partial solution for the key use cases described above already exists today. A
538
648
539
649
Both `extends` and `is` are supported in Firefox and Chromium-based browsers. However, this solution has limitations, such as not being able to attach shadow trees to (most) customized built-in elements. Citing these limitations, Safari doesn't plan to support customized built-ins in this way and have shared their objections here: https://github.com/WebKit/standards-positions/issues/97#issuecomment-1328880274. As such, `extends` and `is` are not on a path to full interoperability today.
540
650
541
-
### Future Considerations
542
-
If additional behaviors are introduced in the future, how should potential conflicts be addressed? For example:
543
-
544
-
- **Conflicting semantics**: How should we handle ambiguity when combining command invocation behavior with label behavior? Should the element have an ARIA role of `button`, or none at all since labels lack an implicit ARIA role?
545
-
- **Interaction conflicts**: When clicked, the [Invoker Commands API](https://developer.mozilla.org/en-US/docs/Web/API/Invoker_Commands_API) will automatically trigger command invocation, but should the element also transfer focus to a labeled control (label behavior)? Would this dual behavior create confusion or negatively impact the user experience?
546
-
- **Specification complexity**: How can we define conflict resolution for these combinations without introducing excessive edge cases and complexity?
547
-
548
-
```js
549
-
classCustomElementextendsHTMLElement {
550
-
static buttonActivationBehaviors =true;
551
-
static labelBehaviors =true;
552
-
553
-
constructor() {
554
-
super();
555
-
this.internals_=this.attachInternals();
556
-
}
557
-
558
-
getcommandForElement() {
559
-
returnthis.internals_.commandForElement??null;
560
-
}
561
-
562
-
setcommandForElement(element) {
563
-
this.internals_.commandForElement= element;
564
-
}
565
-
566
-
getcontrol() {
567
-
returnthis.internals_.control??null;
568
-
}
569
-
570
-
sethtmlFor(value) {
571
-
this.internals_.htmlFor= value;
572
-
}
573
-
574
-
// Manual handling of label behavior (command invocation is handled automatically)
575
-
handleLabelClick(event) {
576
-
// Should this also transfer focus to labeled control (label behavior)?
577
-
// Developers must resolve this conflict manually since command invocation
578
-
// is automatically handled by the Invoker Commands API
579
-
if (this.control) {
580
-
// Label behavior: focus the labeled control
581
-
this.control.focus();
582
-
}
583
-
}
584
-
585
-
connectedCallback() {
586
-
// Manual role conflict resolution - developers must decide
587
-
// whether this should be a button or label
588
-
this.internals_.role='button'; // or no role for label behavior?
589
-
590
-
// Manual focus management for button behavior
591
-
if (!this.hasAttribute('tabindex')) {
592
-
this.tabIndex=0;
593
-
}
594
-
595
-
// Manual event handling for label behavior (command invocation is automatic)
These questions are important for long-term design, but they are outside the scope of this proposal. Our goal here is to acknowledge them without attempting to resolve them.
603
651
604
652
## Stakeholder Feedback / Opposition
605
653
- Chromium: Positive
606
-
- WebKit: No official signal
607
-
- Gecko: No official signal, but no objections shared in the discussion here: https://github.com/openui/open-ui/issues/1088#issuecomment-2372520455
654
+
- WebKit: No [official signal](https://github.com/WebKit/standards-positions/issues/513)
655
+
- Gecko: No [official signal](https://github.com/mozilla/standards-positions/issues/1250)
608
656
609
657
[WHATWG resolution to accept `elementInternals.type='button'`](https://github.com/openui/open-ui/issues/1088#issuecomment-2372520455)
0 commit comments