Open lukewarlow opened 1 month ago
See https://demo.lukewarlow.dev/toggle-button.html for a basic demo on what this could look like. The demo uses <toggle-button>
instead of <button type="toggle">
for ease of implementation.
Edit: This is out of sync with the new proposal
Would this potentially interact with invoker? like setting defaultpressed
on the button given the invoke target is muted by default?
<button type="toggle" invoketarget="video-el" invokeaction="mute">mute</button>
<video id="video-el" muted></video>
Great question, yes the intention is that this can work with invokers. We need to work out the specifics. Like should invoking a popover work with a toggle button? My gut is that this maybe shouldn't work because else you'd have two states fighting each other (expanded vs pressed). On the flip side there's some actions that maybe should require a toggle button, like video mute?
Perhaps invokers could handle the pressed state separately and not require type toggle at all. All great questions that I hope to work through a bit during today's OpenUI meeting, and further ongoing discussions.
The Open UI Community Group just discussed Toggle Button Proposal
.
One alternative that came up in discussions is a pressed attribute but rather than a Boolean attribute it's enumerated so it's true, false or mixed (if that's needed still need to work that out).
So that solves the how to signify it's a toggle when off problem mentioned above. Would mean reflecting the value back to the attribute which might not be ideal? But also means no need for the defaultpressed attribute?
The indeterminate state should be included with this. Though not frequently used, if it is ever needed and not included then people would have to completely roll their own control, since aria states are meant to be overridden by native features.
Eg a native checkbox with aria-checked=mixed won’t actually work since the “checked” state of the input takes priority over the aria attribute
This is going to recap some of what we discussed on Masto.
In aria there's the concept of a pressed state that buttons can have. https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed
I encourage you to link to the specs since MDN is sometimes wrong: https://w3c.github.io/aria/#aria-pressed
Note: I'm intentionally ignoring the "mixed" value of the aria-pressed attribute as I'm not sure what actual utility is provides and how much it's used.
If you want parity with aria-pressed
, then you cannot ignore mixed
. Not accounting for it at this step means you are creating a divergent control.
As for not being sure about its utility nor how much it is used, I feel that is necessary to identify before writing this proposal. In short, this proposal is premature without it.
Quick use cases:
New
:pressed
pseudo class
And :mixed
or lean on the existing :indeterminate
.
New defaultpressed DOM attribute This would be a boolean attribute and would change the default to aria-pressed="true" rather than false.
Needs to support mixed.
New pressed IDL property This would be a read/write boolean IDL property, with no corresponding DOM attribute. It would get/set the internal pressed state of the button.
Also not boolean because it also needs to supported mixed.
For the switch component, the HTML proposal (And recently shipped implementation in Safari) uses a new attribute on the checkbox input.
Additional notes on switch
beyond what you provided:
switch
role in ARIA);However, as previously stated toggle buttons aren't form participants and so probably won't be used inside of a form element.
They will. Partly because many authors go for a control based on look, and if this looks like the control they want then they will stuff it in a form (think of cookie consent forms). If it looks the same as a switch but allows a third state, then even more reason I expect it will get used in a form. As such, it will be critical to provide detailed author guidance.
The button needs to hold a default pressed state (else AT wouldn't announce it as a toggle button unless in the pressed state).
This is an oversimplification.
So the button doesn't need to hold a "pressed" state if you are minting a new control. Instead, you will need to work the HTML-AAM spec and ARIA spec to identify how that maps.
In other words, the statement "else AT wouldn't announce" is only true if you explicitly specify it not to. Or you don't collaborate, of course.
If we just had
pressed
to indicate aria-pressed="true" then this wouldn't be possible.
So far this proposal assumes a binary control (true/false). But since you are trying to replicate ARIA, you are making a trinary control (true/false/mixed). Which means you might want to consider pressed="true|false|mixed"
to mimic ARIA.
Based on that discussion I'll write an updated version of the proposal and post it as a comment here and update the original post.
To clarify when I say the pressed Boolean attribute won't work I mean assuming it's just a standard button under the hood exactly because the aam mappings won't exist for it without some new indicator.
I appreciate the feedback this is exactly the conversation I wanted to spark with this issue.
I feel that is necessary to identify before writing this proposal. In short, this proposal is premature without it.
Worth keeping in mind OpenUI is an incubator group so I feel the barrier for a "proposal" here is lower, it's more about starting the conversation to get to a solid end result. Proposal is perhaps a bad word for it though
Worth keeping in mind OpenUI is an incubator group so I feel the barrier for a "proposal" here is lower […]
My mistake. I am coming at this from a standards world.
Toggle Buttons
Firstly I'd like to acknowledge that the term "toggle button" is incredibly overloaded, so the TLDR is a native "aria-pressed" toggle button.
Problem
It's common in UI design to have buttons which toggle a binary state. These are implemented in varying ways across the web, some involve JS (aria-pressed, or changing the button label), some are semantically questionable (checkbox, switch), and some are both (styled divs).
In aria there's the concept of a pressed state that buttons can have. https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed this requires JS to toggle on and off currently.
Use cases
See https://github.com/openui/open-ui/issues/1039
Basically any button where a single state (which should be reflected by a fixed label) is being toggled. e.g. a mute button.
Proposed Solution
New
"toggle"
value for the button element's type attribute.Functionality this would provide atop the "button" value:
Implicit pressed state, defaulting to false. This corresponds to aria-pressed attribute.
Pressing the button would toggle the pressed state (e.g. false -> true for first click)
The buttons default styling would include an indicator of this pressed state.
New
:pressed
pseudo classThis would apply to toggle buttons which are in the pressed state. This would be used by the UA stylesheet to provide the aforementioned styling indicator.
When in the mixed state this element would match
:indeterminate
and not pressed.New
defaultpressed
DOM attributeThis would be an enum attribute and would allow the default to be aria-pressed="true" or aria-pressed="mixed" rather than "falss".
It would be reflected to a new defaultPressed IDL attribute.
Ergonomically it might be good if the missing value default was "true"?
New
pressed
IDL propertyThis would be a read/write IDL property, with no corresponding DOM attribute. It would get/set the internal pressed state of the button. Accepted values would be "true", "false", "mixed".
We could alternatively make pressed be boolean and use a separate indeterminate property (like checkbox) but I've heard this might not be desirable DX.
Alternatives
Reuse checkbox
HTML already has a sort of state toggle, the checkbox input. For the switch component, the HTML proposal (And recently shipped implementation in Safari) uses a new attribute on the checkbox input. Why don't we follow the same path here?
A switch and a checkbox are generally intended to be form participants, toggle buttons are NOT (as such my proposal introduces no form value concept for toggle buttons)
Toggle buttons are also buttons for this reason it doesn't make sense to use input as the base element.
(opinion) The switch should probably have been a new input type, a button type/attribute addition or a new element altogether. The selling point is that it degrades gracefully to a checkbox but a checkbox and switch have different semantics (delayed action vs immediate action)
New toggle attribute on
<button type="button">
Why am I proposing a new type rather than a new attribute for buttons?
One downside to a new button type is that invalid button types resolve to submit not button, this means in older browsers these toggle buttons could erroneously submit forms.
However, as previously stated toggle buttons aren't form participants and so might not be used inside of a form element as much. They also (currently) still require some JS to do an action and so you can prevent default the submission in older browsers.
Aside from the potential form submission in unsupporting browsers the new type feels like an ergonomic win compared to a new attribute.
If we take a default pressed button as an example:
<button type="toggle" defaultpressed="true">
vs<button type="button" toggle defaultpressed>
Using a new type also means that we can't try and turn reset or submit or future button into a toggle button accidentally.
New pressed enum attribute?
This is the strongest alternative imo. It would alleviate the requirement to have a defaultpressed attribute and IDL property.
<button type="button" pressed="true">
where pressed takes "true", "false", and "mixed", this would reflect the IDL property (and change when the button is pressed too).While it's potentially clearer (no use of the term toggle), it does rely on state reflection, which is undesirable imo, it also doesn't match most other elements on the web (aside from dialog and details).
Questions
Should
:pressed
also apply if an explicit aria-pressed attribute is applied to elements with a role button?Should this fire an event? If so, which?
How should defaultpressed attribute work if you change it once parsed? Should pressed update to match until the user clicks the button? (this is I believe how checkbox works)
Should defaultpressed instead be called initiallypressed? This might depend on the previous question. default is more consistent?
How does this work with invokers?
What should the default styling be?
What should the IDL attributes do for non-toggle buttons?
If the button is mixed what should clicking it do
Old Version
# Toggle Buttons Firstly I'd like to acknowledge that the term "toggle button" is incredibly overloaded, so the TLDR is a native "aria-pressed" toggle button. ## Problem It's common in UI design to have buttons which toggle a binary state. These are implemented in varying ways across the web, some involve JS (aria-pressed, or changing the button label), some are semantically questionable (checkbox, switch), and some are both (styled divs). In aria there's the concept of a pressed state that buttons can have. https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed this requires JS to toggle on and off currently. ## Use cases See https://github.com/openui/open-ui/issues/1039 Basically any button where a single state (which should be reflected by a fixed label) is being toggled. e.g. a mute button. ## Proposed Solution ### New `"toggle"` value for the button element's type attribute. Functionality this would provide atop the "button" value: - Implicit pressed state, defaulting to false. This corresponds to aria-pressed attribute. - Pressing the button would toggle the pressed state (e.g. false -> true for first click) - The buttons default styling would include an indicator of this pressed state. Note: I'm intentionally ignoring the "mixed" value of the aria-pressed attribute as I'm not sure what actual utility is provides and how much it's used. ### New `:pressed` pseudo class This would apply to toggle buttons which are in the pressed state. This would be used by the UA stylesheet to provide the aforementioned styling indicator. ### New `defaultpressed` DOM attribute This would be a boolean attribute and would change the default to aria-pressed="true" rather than false. It would be reflected to a new defaultPressed boolean IDL attribute. ### New `pressed` IDL property This would be a read/write boolean IDL property, with no corresponding DOM attribute. It would get/set the internal pressed state of the button. ## Alternatives ### Reuse checkbox HTML already has a sort of state toggle, the checkbox input. For the switch component, the HTML proposal (And recently shipped implementation in Safari) uses a new attribute on the checkbox input. Why don't we follow the same path here? 1. A switch and a checkbox are generally intended to be form participants, toggle buttons are *NOT* (as such my proposal introduces no form value concept for toggle buttons) 2. Toggle buttons are also *buttons* for this reason it doesn't make sense to use input as the base element. ### New toggle attribute on `