With HTML alone there is no way to disable a hyperlink (an
<a href> element), and have it be both exposed as a “link” and as “disabled”. Now, setting aside whether or not disabling a link is even a good idea (opinion: it’s probably not), that hasn’t stopped people from
needing to do it.
The reasons for disabling a link that I’ve encountered can generally summed up as:
- We want to let people know there’s a navigation item or items that they don’t have access to (yet). For instance, “You’re on our free/low priced plan. But you may have noticed the link to the premium content here? Well it’s not for you! HA HA, it doesn’t work unless you pay up!”
- This disabled control (e.g.,
<a href=# onclick=lol>Play</a>) should probably be a button, but we haven’t figured that out yet. Reflection on one’s choices and acknowledging knowledge gaps by a self defined “10x” dev is hard.
- There is no good third reason I can think of, but I wanted three bullet points. Fill in the blank? I’m sure you have your own stories.
Anyway, reasons. You might have to “disable” a link for reasons. So how should this be handled in a way that will not result in a quirky user experience (rather, the least quirky experience possible when disabling a link), or an unnecessary amount of work for you.
How to disable a hyperlink
For starters, we need to create a link before we can even think about disabling it. With HTML, a link that users directly interact with is created with the
<a> element with a valid
<a href="...">Learn something!</a>
Even if you’re making some sort of SPA, a link should likely still be output as an
<a href>. Sure, you may not write that particular element yourself. But fortunately, people figured this out for you. Neat, huh?
Anyway, great! We have our link. Being built with the native
<a> element, with a valid
href, it will be exposed to assistive technologies (AT) with an implicit
link role. The link will be keyboard focusable by default (or “by default” after you futz with some macOS settings), and actionable by use of the Enter key. All as expected.
Now it’s time to disable it. For that, you might think “oh, let’s use the
disabled attribute!” Which is awesome that you’d think to even do that. Use an HTML attribute, rather than just throwing an
is-disabled class on the element.
However, as bubbles are meant to be burst – that attribute is not allowed nor respected on an
<a> element. So if you did something like the following:
<a href="..." disabled>...</a>
Nothing would happen. The link would still be interactive, keyboard focus could still navigate to it, and it would not be exposed as being in the disabled state. And that would be correct, because the attribute is not allowed on the element.
OK, so what about
<a href="..." aria-disabled="true">...</a>
Well, not to get ahead of ourselves here, but “yes”.
aria-disabled will be necessary to expose a link as being disabled, but using it now, on this link, still isn’t quite right, yet. You see, ARIA does not alter the native functionality of HTML elements. So, while specifying
aria-disabled=true on the link will allow it to be announced as “disabled”/”dimmed”, it won’t do anything to actually strip its functionality. Without additional effort on the developer’s part, a user could still click, tap, or navigate to it and activate it via keyboard.
aria-disabled to a link (or any other interactive element) without actually disabling it is kinda like lying. Similar to styling a link to appear disabled, but not actually properly disabling it. Oh, and by “kinda like lying”, I mean it’s actually lying. And we don’t want to do that.
I’m going to suggest we pick the smart route. You can do a search for the clever version later, if you like.
Actually disabling a native link
The most straight forward way to ensure that a link can no longer be activated is to remove its
href attribute the
<a> element represents a “placeholder” for where a link would semantically be if it were relevant. Placeholder links are meant to be exposed as generic elements to the accessibility API (similar to a
<span>), though not all implementations are entirely in sync with this expectation.
<a> elements without
href attributes. Boo.
So, in these situations even when correctly “disabling” the link, it could well produce an announcement as if it were an interactive element (check out this test CodePen with your screen reader and browser pairing of choice to hear how the placeholder links are announced).
That’s not ideal. And truthfully, for specific situations it may also not be ideal that the “link” role is not announced. Particularly if the element is still styled to resemble a link, but in a visually “disabled” state.
Now it’s time to bring out the ARIA.
Correctly using ARIA
As mentioned, HTML does not allow for a hyperlink to be disabled. However, the ARIA specification’s
link role does allow for the role to be set to the disabled state. This is one of those instances where the allowances of HTML and ARIA are not in sync. There are often good, if not at least understandable reasons for such instances of misalignment. But more on that another day.
Back to our placeholder link:
It has been correctly stripped of its hyperlink functionality, and
link role, by removing the
href. But, we need to make sure that:
- it is not inconsistently announced as “clickable”
- it is exposed as a “link”
- and it is exposed in the “disabled” state
We can take care of the first two bullet points by specifying
role=link on the
<a role="link">Learn something!</a>
You might look at this and think “but aren’t we not supposed to use a
role on an element that already exposes that role? Won’t [ automated checker ] yell at me?”
Generally, yes. It is not recommended to specify an explicit role that matches an element’s implicit ARIA role. However, in this case it’s fine because
<a href> has an implicit “link” role, but
href does not. If an automated checker does yell at you for
<a role=link>...</a> then that’s a bug with that tool.
role=link overwrites placeholder link’s implicit
generic role. Which also stops the AT that were trying to manage potential author errors from producing the “clickable” announcement. Now we have a “link” that has no functionality, and cannot be keyboard focused. Time to communicate that:
<a role="link" aria-disabled="true">Learn something!</a>
Where it is not recommended to use the
aria-disabled attribute on an
<a href> element, it’s perfectly fine to use this attribute on a
role=link. This element will now be exposed with a “link” role, and it will announce itself as “disabled”/”dimmed”. And since there is no
href, it will not be inherently interactive or even result in a context menu with link-related menu items, when right clicked.
And there you have it. A “disabled link”.
Use wisely, carefully, and hopefully rarely.