Recently I stumbled across some false guidance suggesting that product designers should permit focus to land on disabled controls. I would love to take this opportunity to emphatically debunk that idea.
Disabled controls should be removed from the focus order, because this is the behavior dictated by the native web pattern for the disabled
attribute. This is the built-in behavior, it is familiar to users, and it would require deliberate intervention to change the pattern. Reinserting a button or control into the focus order would be like suggesting that people start walking backwards. Why would you do this? City infrastructure is built for support frontwards-walking, it is built into our genetic code and our anatomy: just let it be, unless you have a really good reason to change it.
My best guess is that the proposal to include a disabled control in the focus order arose from a developer attempting to build a control from scratch, because the markup required to do so would leave focusability as optional.
Custom Web Controls
This gives us yet another good reason not to build basic controls from scratch. I will say, it is unrealistic to expect developers to shun custom elements completely, but some controls provide so much native functionality that it is foolish to try to build them from the ground up.
Let’s take a custom button as an example (see code below). Starting with a div
, we must add the button
role, an accessible name, a tabindex="0"
to include it in the focus order, and we must handle click events as well as Enter and Space keypress events in order to mimic native <button>
functionality. Additionally, the developer must style the button’s default state and its focus, hover and (optionally) active state.
<div role="button" aria-label="Close Dialog" tabindex="0" onclick="closeDialog()" onkeypress="return handleKeyPress(event)">x</span>
<script>
function closeDialog() { console.log("Dialog closed.") }
function handleKeyPress() {
if (e.keyCode == 13 || e.keyCode == 32) console.log("Dialog closed.");
}
</script>
…and we haven’t even touched disabled states yet.
The ‘Disabled’ Attribute
When the disabled
attribute is applied to a native <button>
element, it applies the :disabled
CSS pseudo-class to the element. This has the effect of removing the button from the focus order, communicating its disabled state to assistive technology, and transforming the button’s CSS styling. And what developers always seem to forget about the disabled CSS styles is that they affect visibility in Windows’ High Contrast Mode. In High Contrast Mode a disabled button assumes a green border in its default state that changes to white when it is focused.
This only covers buttons, but the disabled
attribute is supported by <button>
, <command>
, <fieldset>
, <keygen>
, <optgroup>
, <option>
, <select>
, <textarea>
and <input>
.
A developer attempting to custom-build a control’s disabled state will likely reach for the aria-disabled
attribute, and this is a mistake. In practice, aria-disabled
should be avoided in favor of disabled
, as the latter HTML attribute provides a visual representation of disabled state, communicates this to assistive technology and removes it from the focus order — while the ARIA attribute only communicates the state to the screen reader. And there is no point in using both, because in this case the aria-disabled
attribute is ignored.
Custom disabled element states (and custom controls in general) should be avoided, because there is too much that can go wrong. When considering the appropriate pattern for users of assistive technology, developers should first consider existing web patterns. Since the disabled
attribute removes a button from the focus order, then this should be the universal behavior.