Skip to content

Make sure disabled buttons are always focusable by default #56547

@afercia

Description

@afercia

Description

Splitting this out from #56396 (comment) for a broader discussion. Cc @ciampo @andrewhayward @alexsanford @joedolson

As noted in this comment, focusable elements that are dynamically added or removed / hidden / disabled potentially trigger a focus loss and impact other utilities, components and hooks like the @wordpress/dom utilities focusable and tabbable, useConstrainedTabbing and useFocusOutside. There have been several cases so far where focus losses were triggered because of this. When using a keyboard, focus losses are a terrible user experience that needs to be avoided.

RIght now, the Button component has an __experimentalIsFocusable prop that is meant to render aria-disabled instead of the disabled attribute to keep disabled buttons focusable. In #56396 (comment) a proposal was made to make this prop true bu default:

any potential drawbacks if the Button component had the __experimentalIsFocusable prop true by default?

I'm not sure this would be ideal. If I understand correctly, the current situation is something like this, in pseudo-code:

<Button disabled __experimentalIsFocusable />

Conceptually, I find this confusing. disabled is a HTML attribute, it should not be used as a prop name. I think this is source of confusion for many new-generation contributors that are used to just use components props without a deep understanding of what happens in terms of the DOM and the rendered HTML.

If I understand correctly, what has been proposed in #56396 (comment) would be, in pseudo-code, something like this:

<Button disabled __experimentalIsFocusable={ true } />

And when both props are true, under the hood the button would use aria-disabled. I'd think this would be even more confusing because it dosn't explain what it does and it doesn't educate contributors.

What we need for accessibility

In 90% of the cases, the disabled attribute should not be used. In fact, in the editor we established a pattern for 'disabled' buttons to:

  • use aria-disabled to keep buttons focusable
  • noop the disabled button

This is in place for two reasons:

  • avoid focus losses
  • discoverablity of disabled controls

See W3C: ARIA Authoring Practices Guide (APG) > Developing a Keyboard Interface > Focusability of disabled controls
https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#focusabilityofdisabledcontrols

Considering that this is what we'd want in 90% of the caees, I'd like to propose to consider to reverse the logic.

Instead of making the exception the default behavior, disabled buttons should render aria-disabled and be noop-ed by default.

  • The disabled prop should be renamed to isDisabled and implement the above behavior.
  • A new prop isFullyDisabled (or a better name) should be used to render a disabled attribute instead of the above.
  • A new custom eslint rule should be added to detect the usage of the isFullyDisabled prop or any other disabled HTML attribute being rendered. The rule should prevent rendering a disabled HTML attribute unless an eslint-disable* comment is added that requires to specify the disable reason.

I may entirely be missing something but I think the editor needs a solid way to prevent focus losses that are often triggered by disabling buttons while they are focused. Also, contributors education is key and all this should be very well documented.

Any thoughts and feedback more than welcome.

Step-by-step reproduction instructions

Not applicable.

Screenshots, screen recording, code snippet

No response

Environment info

No response

Please confirm that you have searched existing issues in the repo.

Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

Yes

Metadata

Metadata

Assignees

No one assigned

    Labels

    [Focus] Accessibility (a11y)Changes that impact accessibility and need corresponding review (e.g. markup changes).[Package] Components/packages/components[Type] EnhancementA suggestion for improvement.

    Type

    No type

    Projects

    Status

    Inbox (needs triage) 📬

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions