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 wanting 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.

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 href attribute.

<a href="...">Learn something!</a>

Even if you’re making some sort of single page app (SPA), a link should generally still be rendered to the DOM 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 aria-disabled then?

<a href="..." aria-disabled="true">...</a>

Well, not to get ahead of ourselves here, but “yes”. aria-disabled will be necessary to expose a hyperlink as being disabled, but using it now, on this link, still isn’t quite right. Yet.

ARIA does not alter the implicit functionality of HTML elements. So, while specifying aria-disabled=true on the link will allow it to be exposed to assistive technologies as “disabled”/”dimmed”, it won’t do anything to actually negate its functionality. Without additional effort on the developer’s part, a user would still be able to click, tap, or navigate to the hyperlink by keyboard and activate it.

Adding 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 disabling it. Oh, and by “kinda like lying”, I mean it’s actually lying. Don’t be a liar.

Now, we could jump through the hoops and write the JavaScript necessary to completely disable that link (even going as far as to ensure the link can’t be right-clicked and then have the default context menu with all the link menu items in it pop up). We could do that, and be rather clever. Or, we could instead be smart about it.

I’m going to suggest we be smart. You can go read someone else’s blog posts on how to be clever.

The most straight forward way to ensure that a link can no longer be activated is to remove its href attribute.

<a>Learn something!</a>

Without an href attribute the <a> element represents a “placeholder” (I’ve actually talked about this before) 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.

For instance, while the “link” role will generally not be announced when encountering a placeholder link (which is correct), some browser/AT combos will still produce a “clikcable” announcement. Unfortunately, why this is announced does make some sense, as it is likely the AT trying to mitigate against poor authoring practices. For instance, when developers attach JavaScript click events to <a> elements without href attributes. Boo! Liar! Booooo!

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:

<a>Learn something!</a>

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> element.

<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 [ insert 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 <a> without href does not. If an automated checker does yell at you for <a role=link>...</a> then that’s a bug with that checker, not with your code.

Specifying the 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.