It’s a well known HTML good practice to always use the disabled attribute on a button when the action cannot be performed.

Let’s pose this a few seconds and ask ourself if this is really the best way to provide a good experience to your users.

What’s wrong to use the “disabled” attribute to properly avoid user to trigger an action that is not available?

<button type="button" disabled="disabled">
  Some action
</button>

After all, it seems that just disabling a button is the best way to do and this is the standard HTML behavior and it exists on all native UI frameworks.

So why not always using it on a webapp? Let’s have a look.

Why you should use ‘disabled’?

This attribute will bring out-of-the-box behavior for free:

  • action is blocked when user tries to click.
  • visual style clearly reflect that button is not available.

Why should you not use ‘disabled’?

  • Disabled button will not get focus or other event.
  • As such, they do not participate anymore in “Tab” navigation.
  • As action cannot get focus or events, this prevent you if you ever wants to provide extra guidance when users reaches the action.

The later is typically done by using a tooltip, or popup, that is shown when user clicks or focus on the button.

Note: for this last point, it can be quite important to explain to the user why an action is not available:

  • flow cannot continue until sale conditions are approved by the user.
  • payment is disabled because payment details are not provided.
  • etc.

Disabled buttons can be an issue for accessibility and user experience consistency.

why some buttons would be accessible when some are not? User could be confused.

Check bellow the small demo:

  • Nvda voice output is displayed on the left
  • First, I navigate with arrows using Nvda cursor: buttons are properly read, but not the tooltip.
  • Second, I use b and Shift+b to navigate on button: tooltip is not read.
  • Last I use Tab and Shift+Tab to navigate buttons: disabled button is not part of Tab navigation.
Nvda dealing with a disabled button.

What is the alternative?

It all depend on expected user experience scenario, but I think it’s much better to not use HTML disabled attribute and only specify disabled through aria-disabled.

<!--
  This button is not disabled.
  It will be able to show a Bootstrap tooltip on focus
  (tooltip is configured thanks to `data-` attributes.
  Check the Bootstrap documentation for details.
-->
<button type="button" 
        aria-disabled='true'
        data-bs-toggle="tooltip"
        data-bs-title="This option is not available">
  Not available action
</button>

Then how this implementation compare with the previous disabled button implementation?

  • Nvda voice output is displayed on the left
  • First, I navigate with arrows using Nvda cursor: buttons are properly read, but not the tooltip. Disabled button is properly exposed as disabled.
  • Second, I use b and Shift+b to navigate on button: tooltip is not read.
  • Last I use Tab and Shift+Tab to navigate buttons: disabled button is now part of Tab navigation and tooltip display is now properly triggered.
Nvda dealing with a button flag as aria-disabled.

The exact same behavior can be noticed when using VoiceOver and Safari on a Mac:

  <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
    <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/-jMU5x6L7BQ?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
    ></iframe>
  </div>

Conclusion

Final choice is yours, and will depend on what you expect for the UI/UX.

Keep in mind this way to implement a disabled button, especially if you need minimal interaction on such action.

Reducing the user confusion and improving the UI consistency should be your first criteria.


Some links: