Press Buttons (Explainer)
- Authors: Luke Warlow
Table of Contents
Background
It’s common in UI design to have buttons which toggle a binary state.
These are implemented in various ways across the web. Some involve JavaScript (e.g. using aria-pressed
or changing the button label), some are semantically questionable (e.g. using a checkbox or switch), and some are both (e.g. using styled div
elements).
In aria there’s the concept of a pressed state that buttons can have, this requires JavaScript to toggle on and off currently.
Goals
- Provide an ergonomic way to create buttons that toggle a binary pressed state.
See Also
See the Toggle Button pattern in ARIA APG.
See Muan’s Press Button Proposal.
API Shape
Press button type
New "press"
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.
CSS Pseudo Class
When a press button is in the pressed state, it will match the :pressed
pseudo class.
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 attribute
This would be an enum attribute and would allow the default state to be aria-pressed=“true” or aria-pressed=“mixed” rather than “false”.
It would be reflected to a new defaultPressed IDL attribute.
It’s missing value default would be “true”, allowing it to be used as a boolean attribute in the simple case.
New pressed
IDL property
This 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”.
UA Styles
The UA stylesheet would include a rule for button:pressed
which would style the button as pressed.
Example 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.
The Choices made in this API
Alternatives Considered
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, press buttons are NOT (as such my proposal introduces no form value concept for press buttons)
Press buttons are also buttons for this reason it doesn’t make sense to use input as the base element.
press boolean attribute
Why are we 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 press buttons could erroneously submit forms.
However, as previously stated press buttons aren’t form participants and so probably won’t be used inside of a form element 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 older 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="press" defaultpressed="true">
vs <button type="button" press defaultpressed>
Using a new type also means that we can’t try and turn reset or submit or future button into a press button accidentally.
pressed enum attribute
This is the strongest alternative. 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, 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?- I’m not aware of any other state pseudo doing this?
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 command invokers?
What should the default styling be?
What should the IDL properties do for non-toggle buttons?
If the button is in the mixed state what should clicking it do?