Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Toggle/cycle buttons #10206

Open
annevk opened this issue Mar 15, 2024 · 55 comments
Open

Toggle/cycle buttons #10206

annevk opened this issue Mar 15, 2024 · 55 comments
Labels
addition/proposal New features or enhancements topic: forms

Comments

@annevk
Copy link
Member

annevk commented Mar 15, 2024

Some of the discussion in #4180 focused on what some platforms consider to be toggle/cycle buttons, which are somewhat distinct from switches. (A demo the WebKit team published of what might be didn't help. I have attempted to correct this with WebKit/WebKit@58cbb3a.)

For instance, https://developer.apple.com/design/human-interface-guidelines/toggles goes into this distinction a bit for Apple platforms. Whereas switches are rather tied to on/off, toggle/cycle buttons have a wider range. Consider a typical play/pause button for instance.

It would be quite reasonable for HTML to also offer a toggle/cycle button control, which also have their own distinct "native appearance" and are exposed differently to assistive technology.

@LeaVerou
Copy link

Interesting, I had prototyped a similar component a while ago: https://nudeui.com/cycle-toggle/

Presumably a native solution should use <option>. Which makes me wonder if it should be a presentation mode on <select> so that it can fall back to a regular <select> if not supported.

@keithamus
Copy link
Contributor

Just recently commented on openui/open-ui#957 with a similar idea:

Allowing a list of options inside of a button, and having the button cycle between those might be worthwhile:

<button type=toggle>
  <option value="play">Play</option>
  <option value="pause">Pause</option>
</button>

Providing <options> allows for a computed value, an easily computed accname, a well understood transition of state, and it is webcompat (to my knowledge).

We are looking at adding richer media to <option> for <select> so it would be a net gain here too.

Having option also allows for N states, which may either sell you on the idea or put you off it entirely 😉 :

<button type=toggle>
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three<option>
</button>

@scottaohara
Copy link
Collaborator

Definitely could be useful to have this. I’d expect a cycle button to be for something like a play/pause button, and the “state” of the button is express through a name change event in the a11y tree.
Would expect different for a toggle button though, which would be more like pressing a bold button for a rich text editor, where a pressed / not pressed state would be exposed with no name change expected.

@lukewarlow
Copy link
Member

can fall back to a regular <select> if not supported.

I guess it depends on the specifics but I feel (just a hunch so could be very wrong) like button would be the correct base element not select?

@LeaVerou
Copy link

can fall back to a regular <select> if not supported.

I guess it depends on the specifics but I feel (just a hunch so could be very wrong) like button would be the correct base element not select?

A button provides a closer presentation but not the actual functionality of being able to select, so it’s not useful as a fallback.

@lukewarlow
Copy link
Member

To do anything with the cycle button you'd still need JavaScript (or invokers) so a button as a fallback wouldn't really impact that? I don't think you'd want this to be a form participant which is another reason why buttons are better than selects?

Invokers which could work with these also only work on buttons?

@scottaohara
Copy link
Collaborator

It’d be really strange to have a select as a fallback for a play/pause button. The select would have no accessible name without a label. I dunno, the ux / functional expectation of what a select vs button represent are quite different. I’d lean away from that idea in favor of a default that is closer to what users would expect.

@Yay295
Copy link
Contributor

Yay295 commented Mar 16, 2024

A button might work for a toggle with two states, but not for a cycle with more than two states.

@lukewarlow
Copy link
Member

A button might work for a toggle with two states, but not for a cycle with more than two states.

I'd be curious what your reasoning is for this? Is it aria limitations or UX issues you envisage?

@Yay295
Copy link
Contributor

Yay295 commented Mar 16, 2024

I was thinking of a checkbox, not a button, for a two-state toggle. A button would work the same for two items as for more than two items.

@Yay295
Copy link
Contributor

Yay295 commented Mar 16, 2024

<input> supports using a <datalist> with its list attribute, so maybe that would actually be the better base element. It also works for backwards compatibility because an <input> with an unknown type defaults to text, which is a valid type for the list attribute.

This:

<label for="ice-cream-choice">Choose a flavor:</label>
<input list="ice-cream-flavors" id="ice-cream-choice" name="ice-cream-choice" type="cycle" />
<datalist id="ice-cream-flavors">
  <option value="Chocolate"></option>
  <option value="Coconut"></option>
  <option value="Mint"></option>
  <option value="Strawberry"></option>
  <option value="Vanilla"></option>
</datalist>

is "valid" HTML already, so people could start using it right away while browsers work on implementing the "cycle" (or other name) type.

@lukewarlow
Copy link
Member

One thing to say, which I know might be contentious, while graceful degradation is important there are limits to what is possible and I think we should be careful not to base future API design on historical happenstance. If we can come up with something that also has a nice degradation great, if not I don't think that's awful.

@scottaohara
Copy link
Collaborator

scottaohara commented Mar 18, 2024

Agree with Luke. Also, @Yay295's example markup of ice cream choices seems like a good example of what people should not be doing with this sort of 'toggle' button. If you have that many choices, why aren't you using just a select element? Why make someone have to click on the button 4 times to get to what they want (vanilla), and X many more times if they have an errant click and have to cycle through all the remaining choices, just to loop around to the first, and then redo the all the clicks again till they get to their choice, hopefully without error.

A toggle button that can be used to cycle between different states, 'play/pause', 'mute/unmute', 'yes/no/maybe so' - cycling between two is probably the primary use case, cycling between 3 seems reasonable for instances where there might be a mixed/indeterminate state that could occur. You go beyond that though, and I'd have to wonder if this is really the right tool for the job. E.g., if you have that many choices (beyond 3), have you just recreated a select element, but with worse UX?

@keithamus
Copy link
Contributor

I agree. There's an extremely strong case for 2, a mild case for tri-state, and the falloff beyond that is very high. One questions if it is a limitation the platform should impose.

Most convoluted counter demo ever (forgive me):

<button type=toggle onclick="this.append(Object.assign(document.createElement('option'), { textContent: this.value + 1 }))">
 <option>1</option>
</button>

@lukewarlow
Copy link
Member

I'd have to double check but I think I'd need a maximum of 4 states for anything I'd consider a toggle/cycle button. For example take a microphone button you'd have 4 states ("press to unmute", "press to mute", "indeterminate state" -think loading spinner that might show during for example a permission prompt, and maybe as a stretch a permission rejected state?).

Most would probably need 2 with a 3rd something is happening state if it's an async action.

Disabled might be able to account for some of this but that's generally a bad idea.

Then you obviously have the toggle Bold example where the label doesn't change which is much closer to the aria-pressed concept.

@lukewarlow
Copy link
Member

The "something is currently happening" state is something common to other buttons too though so perhaps that says we need a separate primitive for it?

It's common to have a "busy" state on submit buttons to stop you double clicking accidentally for example.

Just something to consider I don't want to take this discussion off course though because I feel like the basic cycle idea is something we can achieve.

@gibson042
Copy link
Contributor

gibson042 commented Mar 18, 2024

A good example of a frequently-encountered cycle button with more that two states is the media player repeat-all/repeat-one/no-repeat button 🔁/🔂, sometimes extended to also include stop-after-current ⤞/⇥.

@scottaohara
Copy link
Collaborator

i'd tend to lean towards a 'separate thing' to communicate a loading/busy state, since i wouldn't expect someone to have to add that to their markup of actions to cycle through.

@gibson042, thanks for pointing out those examples. these are interesting because they show how even cycle can have variants in behavior.

A play/pause button for a video, the current action presented for the control represents a behavior that is not representative of the current behavior. e.g., when the button is "Play", it's because the video is currently stopped/paused. But with those cycle buttons, the currently chosen item represents what the player is doing at that moment, and not what will happen only after the button is pressed.

For these repeat/loop buttons, I can see why someone might expect a select as a fallback. where as with the play/pause, I still wouldn't. Maybe that's even more reason to leave fallbacks up to authors to polyfill, have them provide the necessary fallback (or just fallback to the script that they're already using), rather than make a choice about what the fallback should be and have that be acceptable for some cases, and awkward for others?

@Yay295
Copy link
Contributor

Yay295 commented Mar 18, 2024

Just fyi, I took my example directly from https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist. All I did was add type="cycle" to the input element.

@myfonj
Copy link

myfonj commented Mar 20, 2024

What about <fieldset type="condensed-radio-group">?

I believe that backwards-compatible idiomatic accessible HTML "fallback" markup for this kind of user input is a radio group.

So instead of introducing new types of buttons🟀 or new elements whatsoever, what about introducing new type of fieldset?

<fieldset type="condensed-radio-group">
  <legend>Pick your mood:</legend>
  <input id="r0" type="radio" name="r"> <label for="r0">aaargh</label>
  <input id="r1" type="radio" name="r" checked> <label for="r1">meh</label>
  <input id="r2" type="radio" name="r"> <label for="r2">weee</label>
</fieldset>

This markup can be condensed with CSS alone to visually produce single button-like element without compromising accessibility (at least to a larger degree): For general amusement see this POC sandbox🟄. (It is quite simple: grid with stack of labels, transparent "next" one capturing clicks, visible "previous/checked" one displaying state. Arrow keys still work for switching state. Lack of space/enter keyboard key trigger and fact that user clicks "invisible" elements there are only two drawbacks I see in this POC.)

I guess proper accessible interface would announce only the current state and the possible next (one or all) it would be switched to after activation (what is an information that is not present in the visual image), and prevent skipping states. But even this radio group mock-up feels pretty close to that, I think.


🟀 Reminder: introducing new types of button cannot be backwards compatible, because any "unknown" type value would effectively fall back to "submit" in older user agents.
🟄 Adopted from this old SO question asking for a "Quad-state checkbox".

@tabatkins
Copy link
Contributor

Okay that PoC actually rules. I laughed out loud when I focused into it and saw you'd handled that by adding arrows. ^_^

@Yay295
Copy link
Contributor

Yay295 commented Mar 20, 2024

@LeaVerou
Copy link

I don't think it makes sense to impose an arbitrary limit on the number of options. For one, authors may be using multiple states + JS to emulate some kind of richer interaction.

In terms of the markup, the consideration is, how high a priority is graceful degradation these days and what kind of graceful degradation do we need?

If we want something that degrades gracefully in terms of functionality:

  • A <select> provides the functionality even in non-supporting browsers, but I agree is a very weird fallback for some of these use cases
  • Radio buttons, as shown in Toggle/cycle buttons #10206 (comment) is an extremely verbose solution, and IMO the cost-benefit is not there. We don't want to be stuck with this much boilerplate for time immemorial.

A <button> degrades gracefully in terms of appearance, but not sure what benefit that offers? If anything, having something that looks like it should work, but doesn't, is worse, and if we're going to employ JS to make it work, we may as well use a completely new element so we're not limited in terms of API.

@keithamus
Copy link
Contributor

"The future is longer than the past"; I would prefer to optimize for sensible markup, at the cost of a little more effort for backwards compatibility than optimizing for something that degrades nicely but is overly verbose or unintuitive.

As for picking a new element, I can see the benefit but I imagine it'd just be very similar to <button> no? Both in appearance, focus and management. I also imagine almost anyone writing such a component today would implement it as a <button>. @LeaVerou your <cycle-toggle> uses a <button> under the hood, no? Could you talk more about what you see as the disparity between what <button> offers and what is missing that <select> brings?

@tabatkins
Copy link
Contributor

I assume a button would not gracefully degrade in terms of appearance, so it wouldn't "look like it should work", like if the markup was something like:

<button type=toggle>
  <toggleoption selected>light mode
  <toggleoption>dark mode
</button>

then in down-level browsers it would render as a submit button with the text "light mode dark mode", clearly broken. So, I don't think we need to worry about that as a concern.

And I agree with Keith, in most other ways this'll act most like a button - you focus to it, activate it (repeatedly) to toggle between options, and that's it. It's less like a select, which you focus to, activate to open the picker, arrow to choose an option, then activate to close the picker.

@annevk
Copy link
Member Author

annevk commented Mar 27, 2024

I think WebKit is pretty flexible in how we decide to address these use cases, though if there's a reasonable backwards compatibility story that would be better. Most importantly they need to be widgets rendering-wise.

@aardrian
Copy link

aardrian commented Apr 5, 2024

I read through the comments so far, and my very quick thoughts:

  • I favor a tri-state button (on / off / mixed).
  • The ideas above about adding more states are really arguments about adding values, which is addressed by existing controls.
  • The control containing the accName (versus adjacent in another element) maps to my experience for this in the wild (the "bold" button cited above, for example).
  • The aria-pressed state for <button> already supports tri-state and has platform AAPI mappings.
  • Many other states are additive (as in, a disabled tri-state button would still be on, off, or mixed), so those are distinct.
  • Checkboxes are, IMO, good for setting a value prior to submission, while buttons are (again IMO) appropriate for doing the submission on activation (dodging some issues of committing a change on input, a WCAG risk under SC 3.2.2 On Input, right or wrong).
  • I agree these are distinct from switches (though I apply the prior two bullets for choosing a button or checkbox for a switch role, partly for backward compat).
  • I agree with @scottaohara above on the play/pause scenario.

@tabatkins
Copy link
Contributor

The ideas above about adding more states are really arguments about adding values, which is addressed by existing controls.

No, I think this is incorrect. We already have an on/off/mixed control, in two forms even (checkbox, and checkbox-switch). This discussion is largely about a control with significant values, which don't map well to on/off - for example, light vs dark for a theme switcher. And once you have two values, examples with N values (where N is reasonably small) abound. Large numbers of values clearly should be a select or similar, but switching between 2-4 values or so is totally within the remit of this idea.

If we wanted to more directly address cases like a bold button, which looks like a button but is semantically an on/off checkbox, that's probably well handled by stacking more onto checkboxes - <input type=checkbox button> as an alternative to <input type=checkbox switch>. But that's definitely not the limit of the cases that have been discussed in this thread.

@lukewarlow
Copy link
Member

lukewarlow commented Apr 5, 2024

If we wanted to more directly address cases like a bold button, which looks like a button but is semantically an on/off checkbox, that's probably well handled by stacking more onto checkboxes - as an alternative to .

I'm not sure I agree with this bit there could be value in a "native" aria-pressed style button that doesn't overload checkbox. That being said I agree with the rest of your comment that a cycle button supporting n values is definitely useful.

@aardrian
Copy link

aardrian commented Apr 5, 2024

We already have an on/off/mixed control, in two forms even (checkbox, and checkbox-switch).

Yes…

  1. We have a checkbox (on/off/mixed).
  2. ARIA defines, and AAPIs honor, a toggle button via aria-pressed. But that is not in HTML (yet).

…and no:

This discussion is largely about a control with significant values, which don't map well to on/off - for example, light vs dark for a theme switcher.

That is a binary control (and a separate discussion on whether it should be binary, given user system prefs). The values of "light" and "dark" can be mapped to the on/off state by the author. Here I agree with Scott above: "[…] the 'state' of the button is express through a name change event in the a11y tree."

And once you have two values, examples with N values (where N is reasonably small) abound. Large numbers of values clearly should be a select or similar, but switching between 2-4 values or so is totally within the remit of this idea.

I disagree. I read the arguments for this above and I think the case has not been made. At least not beyond author assumptions (some of which are equating values and states). This is not a judgment; I may simply be missing it.

If we wanted to more directly address cases like a bold button, which looks like a button but is semantically an on/off checkbox, that's probably well handled by stacking more onto checkboxes […]

It's already handled with aria-pressed on a <button> (as I explain above, especially with additional labeling elements). My suggestion is that this issue is actually paving that cowpath in HTML instead of leaving it solely in ARIA, even if we haven't all realized it yet.

@tabatkins
Copy link
Contributor

That is a binary control (and a separate discussion on whether it should be binary, given user system prefs). The values of "light" and "dark" can be mapped to the on/off state by the author. Here #10206 (comment): "[…] the 'state' of the button is express through a name change event in the a11y tree."

I... think you're misreading Scott there, as I'm seeing them say the exact opposite of what you're arguing for here.

In that comment, and in this one from the preceding issue, Scott explicitly talks about needing labels on the separate states, specifically because they're not just on/off switches. In the comment you're linking to, they say a "toggle" button might be just on/off (like a "bold" button in a text editor), but a "cycle" button (like Play/Pause on a video player, or like light/dark on a theme switcher) needs accessible labels.

This thread is largely about discussing the "cycle" button case, to use that terminology. (But people have variously called it "toggle" as well.)

I read the arguments for this above and I think the case has not been made. At least not beyond author assumptions (some of which are equating values and states).

The simplest example given so far is a light/dark mode switcher that toggles between "light", "dark", and "system default". These are three distinct and mutually exclusive states, and all need accessible labels.

@jimmyfrasche
Copy link

Would there be a way for a user to move to the previous state? (useless for n = 2, but the utility increases with n from there)

Would there be a way for an author to programmatically set the next(/previous) state? (I don't know if there's necessary any solid use for an FSM button, but if there are it would be a shame if this was something that almost fits but not quite, especially if this properly handles all the a11y concerns for the changing labels)

@tabatkins
Copy link
Contributor

Would there be a way for a user to move to the previous state?

That's likely a browser UI issue, not the concern of the specification.

Would there be a way for an author to programmatically set the next(/previous) state?

Do you mean moving the widget to its next/prev state, or dynamically changing what the next state will be?

In either case, sure, setting the value of the control programmatically will be possible just like for every other form control. Dynamically changing the set of options would just be a matter of mutating the options in the DOM.

@scottaohara
Copy link
Collaborator

doesn't seem to me that @aardrian misread my comment.

And per my comment in the other thread, what I was saying there is that there needs to be a way to overwrite the implicit 'on/off' state of a switch with author defined text - because that's how people are flubbing switches up now. The label of the switch is not changing in the use case i outlined, but rather extra text is used to accompany the visual change in state.

This is different than a cycle button where the visible label of the button changes, which per my comment here, can be odd since people have implemented cycle buttons to either have the current label represent a different action that will occur when activating the button again (e.g., play/pause). Or, they're being used as awkward select/radio button stand-ins where activating the button doesn't cause the current label to occur - but rather a different action will take place that the user just has to become aware of through futzing with the control. I don't really have anything good to say about that UX. So i'll leave it at that.

@tabatkins
Copy link
Contributor

Okay, "label the on/off states" is consistent with "a cycling button with 2 labeled states", but only insofar as the abstract behavior (toggling between two states by activating the control) is shared by both. A light/dark mode switcher is not an on/off control; it would be inappropriate to reuse checkboxes for this, as it has bad submission behavior, for instance (remember, checkboxes only submit if they're "checked", so one of the states would add to the submitted value; the other would just be omitted from the form submission).

If you're saying specifically that switches should be able to have their labels overridden, because people are doing things like light <input type=checkbox switch> dark, then yeah, that's the discussion in this thread, about a cycling button/switch. You'd still want the value that's actually selected to be submitted in a form, for example, rather than, I guess, the "dark" value getting submitted and the "light" value causing it to not show up in the form data.

The control being discussed in this thread is, semantically and form-behaviorally, a <select>, but with the appearance and user-behavior of a button or a switch.

@scottaohara
Copy link
Collaborator

If you're saying specifically that switches should be able to have their labels overridden...

I wasn't saying that. Actually, I went out of my way to say "The label of the switch is not changing in the use case i outlined, but rather extra text is used to accompany the visual change in state." to try and draw even more attention to the fact that's not what I was saying. To hopefully mitigate further confusion so as to stop conflating what I was saying in the other thread with what's being discussed here, i made a demo - https://codepen.io/scottohara/pen/MWRGedp . Hopefully this helps / identifies that this is particular topic is out of scope for this issue.

You'd still want the value that's actually selected to be submitted in a form, for example, rather than, I guess, the "dark" value getting submitted and the "light" value causing it to not show up in the form data.

I'll point out again that this is at odds with some use cases that were mentioned for a cycle button. Mute/unmute and play/pause. The current label/name that would be displayed for these buttons doesn't represent the current state of the media. This is why I find it problematic to try and say this is semantically equivalent to a select. If it's truly the same as a select, then to adrian's point - that already exists (and if/when select elements can be styled, arguably people could be using that instead).

shrug... not really sure what else to say about this that i haven't already said. Hopefully things have been clarified.

@tabatkins
Copy link
Contributor

All right, I'm still a little confused. Please, feel free to explain things very simply to me here, I won't feel condescended to. It's better than having context you think is implicit but is missing from my model. ^_^

In the codepen you just provided, the accessible label is presumably "Theme", from the label. And it's just an on/off toggle. This is presented to the user... how? And how does the displayed text ("light" and "dark") interact with this?

@scottaohara
Copy link
Collaborator

you've generally got it, per your recent comment. The name/label is "theme" (well, now the name is theme because i just added the missing ID to associate the label with the input field. oops).

The state that is communicated is "on/off" to someone using a screen reader.

Right now "light" and "dark" aren't exposed at all (if the switch is labelled - and the pseudo content is inconsistently exposed as the control's name if the switch lacks an accName via the label element, or aria-label, for example). That was the purpose of the other issue - to create a mechanism so that the "on" and "off" audible states could be replaced with author defined replacements, e.g., "light" and "dark", as was the visual intent of the author.

Again, my own opinions on whether someone should be designing their switches this way are not represented here (though if the pseudo content text is not given more consideration for accessibility, then i will absolutely be advocating for people to not add such text, even though it is "allowed" by CSS). I merely want to have a way to make this accessible so that the aural ui can match the visual ui. That's not really possible right now in a way that's sufficient / doesn't still require someone to hear the 'on/off' state announcement.

If someone were expected to use a cycle button to create this ui, then... that'd be unfortunate to then have yet another type of control that needs to be visually modified to resemble a switch (again, the label for the switch is consistent, so having the label/name become 'dark' / 'light' would then raise questions like 'well is that persistent label (theme in my example) even used then? is it appended to the accName? ignored? '

heck, the role wouldn't likely be 'switch' either, since the cycle button could have more than 2 options to cycle through.

i hope that helps clarify. sorry if this is just a whole buncha more words that aren't landing right.

@LeaVerou
Copy link

LeaVerou commented Apr 10, 2024

Btw a good theme switcher is actually a tri-state control: Auto (OS), light, dark.

Another example I ran into the other day: A music note cycle toggle where clicking cycles through the 4 most common notes (whole, half, quarter, eighth).

I have also seen mood cycle toggles, which inspired the examples in https://nudeui.com/cycle-toggle/

Pretty sure I have also seen examples in the wild where clicking gets you a random option, in which case the control could have any number of options.

Would there be a way for a user to move to the previous state?

That's likely a browser UI issue, not the concern of the specification.

I think that's a pretty important usability issue. The specification can certainly make non-normative recommendations about these.

@tabatkins
Copy link
Contributor

@scottaohara Okay, so as far as I can tell, what you're describing is explicitly not what this issue thread is about, and WebKit's position afaik is that such controls shouldn't be represented with checkbox-switch. (The Apple developer guidelines that Anne cites in their initial post to this thread are fairly explicit about this - they say that switches should be used for on/off toggles in the same way checkboxes are, not as toggles between two arbitrary states. Presenting a checkbox as a switch is just better in some UI scenarios.) See Anne's bugfix for a WebKit demo that explicitly removed an example using a checkbox-switch as a light/dark toggle, because that wasn't intended to be an appropriate use of that control.

So, refocusing here -

  • we want a toggle/cycle control, which can switch between "several" states - at least two, almost certainly 3-4, and potentially more.
  • Each state has a distinct accessible name, and can be connected to displayed text hopefully matching that name.
  • We probably want the selected state to be submittable.
  • Semantically and behaviorally, it should probably be equivalent to @myfonj's POC based on radio buttons, just easier to write with dedicated markup.
  • It might want a similar "display" switcher to what we get with checkbox and checkbox-switch - perhaps the default is a button, but if you have two options you can opt into a switch display instead. (Maybe three could work, with the thumb sitting in the middle for the second option? Maybe four+ would work, with the thumb just gradually sliding across the switch?)
  • Some thought should be put into the distinction between "button represents the current state" (like a theme switcher) and "button represents the state you'll switch into" (like play/pause buttons). I suspect this is fine to sweep under the rug: sighted users have to use context to tell the difference as well, so accessibility-wise we can lean on the same; and the "play/pause" distinction probably isn't the sort of thing you'll use in a submittable form anyway. (And if it is, you'll know what you mean.)

@LeaVerou, great examples!

I think that's a pretty important usability issue. The specification can certainly make non-normative recommendations about these.

Sorry, you should definitely be able to reach the previous state (that is, this control should cycle between the options, not get stuck at the end), but I meant we shouldn't mandate anything in the spec requiring a dedicated UI affordance for going "backwards". As long as the default activation behavior goes "forwards", UAs can do whatever to offer additional options. (For example, perhaps a right-click menu could expose the options directly.)

@LeaVerou
Copy link

Sorry, you should definitely be able to reach the previous state (that is, this control should cycle between the options, not get stuck at the end), but I meant we shouldn't mandate anything in the spec requiring a dedicated UI affordance for going "backwards". As long as the default activation behavior goes "forwards", UAs can do whatever to offer additional options. (For example, perhaps a right-click menu could expose the options directly.)

That's like saying it's ok for <input type=number> to only increment, as long as it wraps at the end. If you can go forwards, you should also be able to go backwards. The spec can definitely recommend that UAs should provide a way to do so, without mandating any specific UI for it. E.g. one UA may do it with a context menu, another with a keyboard shortcut(e.g. Alt + click), etc.

@keithamus
Copy link
Contributor

So, refocusing here -...

I believe my proposed markup meets these requirements, no?

<button type=toggle>
  <option value="play">Play</option>
  <option value="pause">Pause</option>
</button>

@myfonj
Copy link

myfonj commented Apr 16, 2024

So, refocusing here -...

I believe my proposed markup meets these requirements, no?

<button type=toggle>
  <option value="play">Play</option>
  <option value="pause">Pause</option>
</button>

Only if there is a consensus that it is really fine for this spec to be backwards incompatible with current HTML, what sounds like a really bad idea.

This structure would act like a regular submit button with funky content in any currently HTML-compliant UA, as well as in any future UA that will not have (yet) adopted this hypothetical new spec.

I do not believe it makes sense for form element, since it is supposed to hold a value and be submittable. It may, however, make sense for completely new elements, think <video>fallback</video>. But not as a variant of an existing element.

As was already said, most fitting existing element for "visual and interactive extensions" here would be <select>if only it supported rich markup (images, SVG, MathML) in options.

What leaves us again with good old radio group. (After all, select is mappable to radio group anyway.)

To address complaints that label for="X" .. input id="X" is unnecessarily verbose (what's true), just to remind you that if we are OK with breaking support of some (presumably very old) screen readers, we can be bold and nest radio inputs inside label to save bunch of characters:

<togglecyclizator_pretty_much_acting_like_a_manual_inline_mini_carousel>
	<label><input type="radio" name="r" value="0"> Steady.</label>
	<label><input type="radio" name="r" value="1" checked> Ready.</label> 
	<label><input type="radio" name="r" value="2"> Go!</label> 
</togglecyclizator_pretty_much_acting_like_a_manual_inline_mini_carousel>

I really see no other option (pun intended) here.

@LeaVerou
Copy link

I think there are two main categories of use cases presented here:

  1. Actions: Those that are basically buttons where clicking the button changes the button label, e.g. the play/pause example
  2. Fields: Those that are basically form elements that need to be submittable, and the cycle toggle stores a value. E.g. the mood selector, the note example, the theme switcher etc.

I suspect most disagreements are because some people have 1 in mind, while others have 2.

I'd argue that 2 is a more important use case to design around, since 1 is basically already doable with script, there is little benefit to encoding the label changing logic declaratively into the HTML. Whereas 2 has no good, accessible alternative today. It may even make sense to design separate APIs for these, e.g. 1 could be <option> nested in <button>, whereas 2 could be a new element.

A tangentially related use case is the common "button toggle", i.e. buttons that represent boolean states by either staying pressed (true) or not (false). These are often used in a group to offer mutually exclusive selections or additive ones (e.g. a formatting toolbar with bold, italic, etc). Not sure if that's something to take into account while designing this or better left as a separate use case.

@Yay295
Copy link
Contributor

Yay295 commented Apr 16, 2024

A button toggle is basically just a checkbox though, right?

@LeaVerou
Copy link

A button toggle is basically just a checkbox though, right?

As much as a <cycle-toggle> is a <select> 😄

@scottaohara
Copy link
Collaborator

I tend to think you're correct on that point, @LeaVerou - there are these two use cases and where people are disagreeing / misunderstanding each other seems to stem from one's leaning towards either of those options.

Hence it would appear you're much more in option 2 camp - where as I and some others, lean more towards there being a need for option 1, and that option 2 is arguably just a different way of presenting select/radio button groups. Again, another bit of a confusion point that seems to have stemmed from this is that in your demo and the radio group POC - what would be the expected label/name of the control is not what the current chosen item is, but rather "mood" in that example, or the visually hidden legend "pick a number" in the radio POC.

A button toggle is basically just a checkbox though, right?

behavior wise, sure, in that you're toggling between a 'pressed' vs 'not pressed' state (there are similar, but different a11y API mappings for toggle buttons than checkboxes). But generally, they don't often visually resemble checkboxes, nor would they be expected to be form submittable. Prior comments in this thread called out this as a need / could (should) be a separate proposal.

As much as a <cycle-toggle> is a <select>

Depending on which option this proposal goes with, option 1 is not a select - it's closer to a button whose primary action changes to either undo or expand on the prior action the button provided. Option 2 is more similar to a select, with different UX (click x times to reach the option you want, rather than being able to see a listing of the available options).

shrug.

@myfonj
Copy link

myfonj commented Apr 16, 2024

A button toggle is basically just a checkbox though, right?

I think it was also discussed here before: Plain checkbox (or 🍏 switch) has only "on (checked)" and "off (unchecked)" states: there is no way to separately label each individual state, such as "play"/"pause", "light mode"/"dark mode", "love"/"hate", "from the stem"/"from the bottom", "under the roll"/"over the roll", "socks first"/"pants first" etc, what might be quite desirable and even beneficial for the UX/a11y.

@Yay295
Copy link
Contributor

Yay295 commented Apr 16, 2024

By

A button toggle is basically just a checkbox though, right?

I was specifically replying to

A tangentially related use case is the common "button toggle", i.e. buttons that represent boolean states by either staying pressed (true) or not (false).

@SaraSoueidan
Copy link

I agree with all of @scottaohara's previous comments on this.

I read through the comments and am currently wondering why this can't be solved with just one simple attribute on the <button> element.

Currently, the aria-pressed attribute enables us to represent toggles that are either On or Off, such as the Bold button in a rich text editor. aria-pressed is used on the <button> element and the state is exposed to ATs as something similar to "Bold, pressed" and "Bold, not pressed", which is self-explanatory and matches the visual UI.

My only gripe with this is that we currently need to use ARIA (which is supposed to be a temporary polyfill for HTML) and then we need to use CSS to apply the pressed styles to the buttons...

button[aria-pressed="true"] {
 // make it look pressed
}

Personally, I would first propose that browsers implement a native pressed attribute and provide visual affordances to the button when it is in the pressed state. Meaning that: instead of using ARIA we could do something like:

<button ... pressed>Bold</button>

and when the button is pressed, the browser applies visual styles to indicate that it is (those would effectively be the same as the current native :active button styles).

pressed would be similar to disabled in that we toggle its presence on the <button> using JavaScript. When it is present, the button is exposed as a toggle (similar to how buttons are currently exposed as toggle buttons when aria-pressed is present).

The accName of the button in the above example is inside the button, and it doesn't need to trigger an accName change event when the button is pressed.

For cycle buttons, the button's label (and accName) does change and it does need to be conveyed to ATs. So, this may be too simplistic but maybe all we need is an attribute that tells the browser "please fire a name change event when this button's name changes"?

Maybe something as simple as a toggle attribute on the <button> element, or a type="toggle", which can even be built upon and enhanced in the future if needed. The presence of this attribute would fire an accName change event, users will be informed of the accName change, and the visual UI thus matches the aural UI.

I don't think we need to do more to improve the DX here. Whatever the markup looks like, we will almost always still need to write JS to make the button do whatever we want it to do (assuming it's outside a form, of course). We just need to make sure that the dynamic changes are conveyed to AT users when they happen. So, as an author, I'll just implement a toggle button, change the label/accName when the user presses it, and the browser conveys the changes to AT, which in turn convey them to the user. Ultimately, the user experience is what should be front and center.

Speaking of the user experience, I do have one important usability concern with some of the suggestions in the thread:

I think allowing more than 3 states for a toggle button would create a usability nightmare.

If I understand correctly, users will be expected to press the button God-knows-how-many times to get to the value they want, right? So, if there are, say, seven values to cycle through, they'd have to press the button six times to get to the sixth option; and if they accidentally press it too many times, they'd have to cycle back to the beginning to get back to the value they missed. And the bigger question is: how do they know how many times they need to press the button to get to the value they need? how do they know how many options are available? and how do they even know that they can press the button several times? What does the visual affordance look like for such a toggle? If the toggle button looks like a regular button, how does the user know that there are several options available to cycle through?

Most toggle button use cases that are currently used across the web are well-known patterns that the majority of users are familiar with and know how to operate. The Play/Pause button is one of those examples. Users know they can either Play or Pause a video. And by pressing that one button, they understand how it works because, like Scott mentioned earlier, the button's label indicates the current state of whatever the button is controlling.

But a toggle that offers multiple options (like Lea's emotions example, for instance) works completely differently. I strongly believe that any toggle that offers more than two or three options or values is something that <select> and radio buttons are more suited for. In these instances, the user is literally choosing one of several options, not activating an action or changing a state. <select> and radio buttons are designed for those use cases. And, depending on the design and use case, sometimes it may just be more appropriate to use multiple separate regular old buttons!

@aardrian
Copy link

Hi, coming out of left field here and then probably tapping out since I made my points above already.

Since this control will have n options, since those options (what folks are calling "states") will need to have author-supplied accNames, since folks want a button-like UI, since this control will almost definitely get used in forms, is there a reason the Open UI <selectmenu> element couldn't be extended to do the job?

Essentially have a flag to disallow expansion onclick and instead move through the sequence of options. Arrows can still navigate backward and forward, making it easier to get to n+23 from n+24, and a modifier for clicks to do same.

The accName will have to come from the selected option if no <label> or other naming method is used. It can participate in forms or not.

This does not solve the ambiguity of the value / accName conveying if that is the current "state" or the one to be selected (Play/Pause).

@SaraSoueidan
Copy link

SaraSoueidan commented Apr 17, 2024

Since this control will have n options, since those options (what folks are calling "states") will need to have author-supplied accNames, since folks want a button-like UI, since this control will almost definitely get used in forms, is there a reason the Open UI element couldn't be extended to do the job?

If this control is meant to be only used in forms (which sounds different to me from what the issue started with) then I +1 the <selectmenu> suggestion.

This does not solve the ambiguity of the value / accName conveying if that is the current "state" or the one to be selected (Play/Pause).

Agreed.

For the toggle button used outside a form, another suggestion I have for a separate element (instead of an attribute) would probably be a <buttongroup>:

<buttongroup>
    <button>Play</button>
    <button>Pause</button>
    <button>[maybe a third state?]</button>
</buttongroup>

The benefits here are that the fallback will be two separate buttons for the two different actions. <buttongroup> would visually be styled like a regular old <button>, with arrows allowing navigation between states (similar to radio group behavior).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements topic: forms
Development

No branches or pull requests