Page MenuHomePhabricator

Progress elements: Explore alternatives to Button loading states
Closed, ResolvedPublic0 Estimated Story Points

Description

Background goal

There are some use cases where a loading state Button is used to indicate the loading of some sections in projects. We need to decide if a new loading state in Button is needed to solve these use cases or if an existing Codex progress element can be used to solve them.

Use cases

Proposed solutions

An indeterminate Inline ProgressBar could be used to solve both Wikifunctions and Translations cases now. It will appear within the section loading while the Button is disabled:

Acceptance Criteria

  • Find a solution for the flagged use cases in this task and determine if a loading button is needed or if other existing progress elements can be used
  • (if needed) file a task to add a new "loading/pending" interactive state for Codex buttons (T377424)

Future tasks

Event Timeline

I'm in general not a big fan of buttons who are changing their labels dynamically. It's an anti-pattern, a button should never change their label and rather have a clearer user feedback triggered if there's an indication to be made to users.
It's also problematic from an accessibility perspective, as there are no ways to indicate that loading change well to users of assistive technology.

The need for this resurfaced in an internal WMF chat. @AAlhazwani-WMF @SGautam_WMF could you add more information about your needs?

We have a “Select section to translate” button. On click, it runs background checks before allowing translation, in this stage we plan to change its label to something like “Checking contents…” along with label change we are thinking what are our options to show the processing state? Currently we have Default, Active, Focus, and Disabled states.
which existing state can we use, if any? Is it ok to change the background color to Option Tokens/Blue/400 (#6D8AF2) Does this scenario call for a new state? like introducing loading indicator or something? More info. about the issue is in this task - T373509

CCiufo-WMF triaged this task as Medium priority.Sep 23 2024, 2:57 PM
CCiufo-WMF set the point value for this task to 0.Sep 25 2024, 5:08 PM

I echo Volker's concerns above, and think we should consider users of assistive technology as well as all users when determining how best to communicate that they have triggered a process and that process is pending.

We should be very careful about using dynamic button labels for a couple reasons:

  1. As Volker mentioned, we'd need to be able to communicate the change to users of assistive technology smoothly and in a way that works across platforms.
  2. We don't want to change the semantic meaning of the button. A button is meant to trigger an action, and changing the label to indicate that a process is in progress changes the purpose and obscures the original purpose. For example, if you press a button with the label "Select section to translate" and the label changes, you might forget what the button originally said and exactly what action you've triggered.

An example from the WAI ARIA Authoring Practices Guide that has been helpful to me is the case of toggle buttons: you might think that a toggle button that says "Play" should change to "Pause" when toggled on, but really, the toggled state indicates whether "Play" is on or off, so the label should not change.

I have a few proposals, and think we could brainstorm more design ideas as part of this task:

  • Show an inline message indicating what's happening after the button is pressed and during the process.
  • Show a ProgressIndicator within or near the button.
  • Consider disabling the button during the process (I'm not actually sure this is a good idea; need to see what the experience is for users of assistive tech). Edit: we could set aria-disabled, which will communicate to assistive tech that the button is disabled without removing it from the tab order of the DOM (which the disabled attribute does, and is confusing). We'd also need to prevent clicks from emitting events since the button wouldn't actually be disabled.
  • As @SGautam_WMF mentioned above, consider adding a new "pending" state for the Button component.
  • If we must change the label, give specific guidelines on the language to use (e.g. if the button's label is "Submit", change it to something like "Submitting" rather than language that obscures the original action).

Unfortunately, I think this might be a case of "in order to make this accessible, we can't have the sleekest solution."

El T350801#10189149, @AnneT escribió:

I have a few proposals, and think we could brainstorm more design ideas as part of this task:

  • Show an inline message indicating what's happening after the button is pressed and during the process.

This could cause some layout issues since the inline message could not fit in certain cases.

Captura de pantalla 2024-10-02 a las 19.34.03.png (294×477 px, 15 KB)
Captura de pantalla 2024-10-02 a las 19.34.41.png (268×331 px, 8 KB)

I like this option. The only problem with this is that the icon-only button would lose its association with the action, since the loading indicator would replace the button's icon.

Captura de pantalla 2024-10-02 a las 19.35.38.png (624×869 px, 35 KB)

  • Consider disabling the button during the process (I'm not actually sure this is a good idea; need to see what the experience is for users of assistive tech). Edit: we could set aria-disabled, which will communicate to assistive tech that the button is disabled without removing it from the tab order of the DOM (which the disabled attribute does, and is confusing). We'd also need to prevent clicks from emitting events since the button wouldn't actually be disabled.

This option works, and we don't need additional implementation for that since the disabled state is already implemented. Users will easily understand that the button is inactive during the loading state, as they're familiar with disabled buttons. We will prevent from possible errors by disabling the button.

Captura de pantalla 2024-10-02 a las 19.37.12.png (664×892 px, 36 KB)

  • As @SGautam_WMF mentioned above, consider adding a new "pending" state for the Button component.
  • If we must change the label, give specific guidelines on the language to use (e.g. if the button's label is "Submit", change it to something like "Submitting" rather than language that obscures the original action).

Unfortunately, I think this might be a case of "in order to make this accessible, we can't have the sleekest solution."

I've also explored this solution. In case we design a specific style for the loading state, I would combine it with the spinner to clearly indicate why the button style is different. This option could also work since we make it clear that the button is inactive. However, we would need to create a new loading style for all button types.

Captura de pantalla 2024-10-02 a las 19.40.27.png (516×731 px, 27 KB)

Captura de pantalla 2024-10-02 a las 19.40.36.png (454×379 px, 28 KB)


My preference is to disable the button during loading without changing the label, which would avoid making it wider. Since users are familiar with the disabled state, they will easily understand it's inactive. If we want to make the loading state clearer, we could additionally use a spinner within the disabled button.

Captura de pantalla 2024-10-02 a las 19.42.48.png (698×1 px, 43 KB)

FWIW, I think all the options @bmartinezcalvo has provided are good and will defer to others on which one is best.

One note: if we decide to show a disabled state when the button's process is pending, there will be some implementation work involved - we can't just have the developer user set the button to disabled because that will take it out of the keyboard navigation tab order, which could be confusing for users of assistive technology (or any user). Instead, we might want to create a pending prop that, when set to true, sets aria-disabled="true" on the button and disables the click event. This will make the button look disabled, communicate to users of assistive tech that the button is disabled, effectively disable the button's action, and keep the button in the tab order.

Just noting this because any of the proposed solutions will require some implementation work on our end.

I agree with the proposal. I do think providing an animated indicator within the button while it is disabled and loading could be helpful. I think in this case, we would replace whatever is in the button with the loading indicator, be it text, icon + text, or icon only. The button would maintain its original width.

CCiufo-WMF changed the task status from Open to In Progress.Oct 2 2024, 9:29 PM

I agree with the proposal. I do think providing an animated indicator within the button while it is disabled and loading could be helpful. I think in this case, we would replace whatever is in the button with the loading indicator, be it text, icon + text, or icon only. The button would maintain its original width.

We could do this by overlaying the icon and a background on top of the existing label, which would be covered up - that would be necessary to maintain the width of the button (we couldn't just visually hide the label with CSS).

That said, it would obscure the label for sighted users, and I'm concerned about that for reasons described in this comment above. I'd recommend we place the loading icon alongside the label instead.

El T350801#10199393, @AnneT escribió:

I agree with the proposal. I do think providing an animated indicator within the button while it is disabled and loading could be helpful. I think in this case, we would replace whatever is in the button with the loading indicator, be it text, icon + text, or icon only. The button would maintain its original width.

We could do this by overlaying the icon and a background on top of the existing label, which would be covered up - that would be necessary to maintain the width of the button (we couldn't just visually hide the label with CSS).

That said, it would obscure the label for sighted users, and I'm concerned about that for reasons described in this comment above. I'd recommend we place the loading icon alongside the label instead.

I agree with @AnneT, I would not change/remove the button's label to avoid missing the action it triggers.

Captura de pantalla 2024-10-03 a las 18.31.28.png (501×751 px, 33 KB)


So, in case we want to keep the label the same, we have 2 options:

  • Op.1: simply use the disabled state to communicates the loading state. Button’s width remains the same in this case. But the loading status is not clearly indicated since visually it's just disabled.

    Captura de pantalla 2024-10-03 a las 18.29.08.png (363×718 px, 29 KB)
  • Op.2: Combination of Disabled + Spinner. It clearly indicates that the button is not active because it’s loading. But there are 2 small issues with this option:
    • When the spinner replaces the icon in the icon-only button, the action reference is lost. @AnneT since the icon is the only reference for the action in an icon-only button, should we avoid removing it and follow the same approach as with labels, keeping it the same across all states?
    • Text-only buttons become wider when adding the spinner (although I don't view this as a blocker).

      Captura de pantalla 2024-10-03 a las 18.17.07.png (629×1 px, 63 KB)

To be fair, when we disable the button, the label becomes inaccessible from a color contrast perspective. That being said, I'm not against finding a better solution that keeps the label.

What about using a slightly altered variation of an inline ProgressBar at the bottom of the button? Something like this? (You can imagine it animating across the bottom of the button with the subtle padding on the sides and bottom. It would be white instead of blue.)

Screenshot 2024-10-03 at 15.40.21.png (1×1 px, 65 KB)

Too much?

To be fair, when we disable the button, the label becomes inaccessible from a color contrast perspective. That being said, I'm not against finding a better solution that keeps the label.

What about using a slightly altered variation of an inline ProgressBar at the bottom of the button? Something like this? (You can imagine it animating across the bottom of the button with the subtle padding on the sides and bottom. It would be white instead of blue.)

Screenshot 2024-10-03 at 15.40.21.png (1×1 px, 65 KB)

Too much?

We could instead include the progress bar as a background loading within the button. Anyway, I'm not sure about adding a ProgressBar within the buttons, it could not be enough visible in some cases, especially in icon-only buttons or a smaller size of buttons if we design it in the future. Additionally, this progress bar could be confusing when the loading button is within a ButtonGroup or group of other elements.

Captura de pantalla 2024-10-04 a las 16.13.43.png (862×2 px, 109 KB)

I lean towards going with the disabled with spinner option which clearly indicates the inactive and loading status. The only con will be that the icon-only button will lose its action's reference since the icon will be replaced with the spinner.

Another consideration is that the label (and spinner) will not be contrasted enough. But, as recommended in WCAG guidelines: "User Interface Components that are not available for user interaction (e.g., a disabled control in HTML) are not required to meet contrast requirements." Since we want the loading button to be inactive, the minimum 4.5:1 doesn't seem to be necessary.

A lot of good options have been put forward and valid points raised, and like Anne I think any of them would be fine. I wanted to make one more "outside the box button" comment though: should we even be relying on a button to communicate a process is in progress? Some alternative ideas including overlaying a ProgressIndicator over a section or the entire page, or similarly triggering a dialog communicating what process is occurring. Changing the state of the button (for example, to disabled) is really to prevent the user from spam-clicking the button, but that isn't always needed. There are cases where you might want the user to be able to press the button again to restart {whatever process is happening}. I think that coming up with very specific guidelines is going to be hard for this, so maybe we keep the guidelines a bit more general and suggest combinations of button state change + some other kind of indicator. Really this is so dependent on each use case though.

@bmartinezcalvo @DTorsani-WMF sharing some quick feedback, i hope this is helpful!

progress bar button
we might not know (on wikifunctions) how long a process is going to last, so using a spinner might more favorable than a progress bar.

image.png (962×1 px, 119 KB)

alternatively, if we opt for a progress bar-like approach, what about something like an indefinite progress, similarly to the striped progress/background used on OOUI components?

image.png (96×750 px, 13 KB)

variable button width
maybe we could provide a guideline that suggest to only use an icon-only or icon+text button when a loading state is needed?

inactive/disabled state
i'm wondering if we should also consider stopping/cancelling the process. if we opt for disabling the button there might be no way to interrupt the action, eg. cancelling a download.

@CCiufo-WMF it would be interesting to test if on wikifunctions, or @SGautam_WMF example overlaying a progress bar over a section or page might be too disruptive. i personally like the idea that while something is running (in the background) i can still "look around" on the page. i'd suggest to try this function to test how an overlay approach might work out https://www.wikifunctions.org/view/en/Z10309

CleanShot 2024-10-07 at 12.26.14@2x.png (1×2 px, 324 KB)

inactive/disabled state
i'm wondering if we should also consider stopping/cancelling the process. if we opt for disabling the button there might be no way to interrupt the action, eg. cancelling a download.

@CCiufo-WMF it would be interesting to test if on wikifunctions, or @SGautam_WMF example overlaying a progress bar over a section or page might be too disruptive. i personally like the idea that while something is running (in the background) i can still "look around" on the page. i'd suggest to try this function to test how an overlay approach might work out https://www.wikifunctions.org/view/en/Z10309

CleanShot 2024-10-07 at 12.26.14@2x.png (1×2 px, 324 KB)

I actually think what's already implemented in Wikifunctions is a good example of what I meant. The "Result" section that opens up below is communicating that the process is in progress, while the button itself could be disabled or remain active depending on whether or not the process is interruptible.

inactive/disabled state
i'm wondering if we should also consider stopping/cancelling the process. if we opt for disabling the button there might be no way to interrupt the action, eg. cancelling a download.

@CCiufo-WMF it would be interesting to test if on wikifunctions, or @SGautam_WMF example overlaying a progress bar over a section or page might be too disruptive. i personally like the idea that while something is running (in the background) i can still "look around" on the page. i'd suggest to try this function to test how an overlay approach might work out https://www.wikifunctions.org/view/en/Z10309

CleanShot 2024-10-07 at 12.26.14@2x.png (1×2 px, 324 KB)

I actually think what's already implemented in Wikifunctions is a good example of what I meant. The "Result" section that opens up below is communicating that the process is in progress, while the button itself could be disabled or remain active depending on whether or not the process is interruptible.

@CCiufo-WMF yeah agreed that if everything goes well the current result section feels like a good solution! thou if something doesn't work as expected what we currently end up with is an empty result section

image.png (870×1 px, 100 KB)

and that's where i feel a loading button might be more convenient.

image.png (728×1 px, 90 KB)

We’ve been exploring ways to address the Wikifunctions and Translation use cases highlighted in this task. Since the need to implement a button loading state has not been decided yet, we could use existing Codex progress elements to solve these use cases. Given that the ProgressIndicator is not implemented yet (T373218), we could use the Inline ProgressBar to solve both cases now. See the following prototypes with the proposed solutions:

We'll create a separate task to evaluate the need for a button loading state to solve other future use cases.

We’ve been exploring ways to address the Wikifunctions and Translation use cases highlighted in this task. Since the need to implement a button loading state has not been decided yet, we could use existing Codex progress elements to solve these use cases. Given that the ProgressIndicator is not implemented yet (T373218), we could use the Inline ProgressBar to solve both cases now. See the following prototypes with the proposed solutions:

We'll create a separate task to evaluate the need for a button loading state to solve other future use cases.

sounds good, thank you @bmartinezcalvo! while playing around with your prototype i wanted to share a spontaneous idea for the future :) i was wondering if we could use the existing inline progress bar inside the button itself, so that we would not to change any button label, or button icon. and we would reuse existing elements from the system. what i like about this idea is how contextual is the progress indicator, is right on the action that you just interacted with, see attached gif.

CleanShot 2024-10-17 at 11.01.34.gif (800×529 px, 186 KB)

We’ve been exploring ways to address the Wikifunctions and Translation use cases highlighted in this task. Since the need to implement a button loading state has not been decided yet, we could use existing Codex progress elements to solve these use cases. Given that the ProgressIndicator is not implemented yet (T373218), we could use the Inline ProgressBar to solve both cases now. See the following prototypes with the proposed solutions:

We'll create a separate task to evaluate the need for a button loading state to solve other future use cases.

sounds good, thank you @bmartinezcalvo! while playing around with your prototype i wanted to share a spontaneous idea for the future :) i was wondering if we could use the existing inline progress bar inside the button itself, so that we would not to change any button label, or button icon. and we would reuse existing elements from the system. what i like about this idea is how contextual is the progress indicator, is right on the action that you just interacted with, see attached gif.

CleanShot 2024-10-17 at 11.01.34.gif (800×529 px, 186 KB)

Thank you @AAlhazwani-WMF. Since the inline progress bar is included within the Button in your exploration, this would be a new loading state for Button. Since we want to avoid including now a new loading state and we want to reuse existing Codex progress components instead, I recommend including the Inline ProgressBar separated from the Button. I've created this separate task to work on the Button loading state in the future T377424: Button: evaluate the need to create a new loading state. So we will evaluate including the Inline ProgressBar within the Button when working on this future task. Feel free to share this exploration in T377424.

bmartinezcalvo renamed this task from Buttons: provide guidelines on an interactive state for loading to Progress elements: Explore alternatives to Button loading states.Oct 17 2024, 10:16 AM
bmartinezcalvo updated the task description. (Show Details)

We’ve been exploring ways to address the Wikifunctions and Translation use cases highlighted in this task. Since the need to implement a button loading state has not been decided yet, we could use existing Codex progress elements to solve these use cases. Given that the ProgressIndicator is not implemented yet (T373218), we could use the Inline ProgressBar to solve both cases now. See the following prototypes with the proposed solutions:

We'll create a separate task to evaluate the need for a button loading state to solve other future use cases.

Thanks @bmartinezcalvo the proposed solution looks good for select sections to translate part.

Since the solutions proposed for Wikifunctions and Translate have been accepted, I'm solving this task. We will continue the explorations of a possible new loading state for Button in T377424.