Skip to content

Rendering an accessible disabled loading primary button is impossible #7879

@iansan5653

Description

@iansan5653

A typical form design has "Submit" and "Cancel" buttons, where the save button is primary:

typical form cancel/submit buttons, enabled and not loading

This base state is very easy to render with Primer:

<Stack direction="horizontal" gap="condensed">
  <Button>Cancel</Button>
  <Button onClick={submit} variant="primary">
    Submit
  </Button>
</Stack>

However, problems arise when the user clicks submit, if we don't immediately navigate away from the page (ie, a 'soft submit'). Typically we want to do two things at the same time:

  1. Indicate that something is happening
  2. Indicate that clicking the buttons will not perform an action, since an action is already pending

For (1) there is only one available prop: loading. So we will set loading on the primary button:

cancel/primary buttons, primary button has a loading indicator and no text, both are enabled

However, this doesn't solve (2). For this there are three(!) possible props we could try:

cancel/primary loading buttons in three states: aria-disabled makes the cancel button appear disabled but not the primary button, inactive makes both buttons appear disabled but overrides the primary style, and disabled makes both buttons appear disabled

Of these, only disabled actually does what we want visually. aria-disabled has no visual impact when combined with loading, and inactive overrides the primary variant which would be confusing and visually disruptive.

However, disabled is inaccessible for form submit buttons, per the accessibility guidance:

form submit buttons...should never be disabled so as to support accessible error handling

Therefore, it is impossible to render an accessible disabled loading submit button with Button.

Potential solutions

I think there's a few possible good solutions here:

  1. loading buttons already have aria-disabled set by default, so maybe they should just always be visually disabled by default
  2. Explicitly setting aria-disabled should definitely make loading buttons visually disabled
  3. If inactive is the right prop, then inactive should not override variant="primary"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions