Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"The href attribute is required for an anchor to be keyboard accessible." is in contradiction to W3 "Using ARIA" in relation to links #906

Open
asos-francesca opened this issue Nov 11, 2022 · 5 comments
Assignees

Comments

@asos-francesca
Copy link

We have followed the convention laid out for disabling a link to make it accessible, laid out by w3c here: https://w3c.github.io/html-aria/#example-communicate-a-disabled-link-with-aria

This example suggests that a link that would otherwise be active, but currently is not, should be expressed as an anchor with no href, e.g:

In the following example, a hyperlink needs to be communicated as being in the disabled state. HTML does not allow for the use of the disabled attribute on a hyperlink, and using aria-disabled=true would communicate the hyperlink as being disabled to assistive technologies, but would not actually disable the element. The most effective way to both communicate and actually disable a hyperlink would be to remove the href from the a element, creating a placeholder. Then, use ARIA can be applied to this placeholder link to communicate the element's intended role and state.

Communicate a disabled link with ARIA:
<a role=link aria-disabled=true>...</a>

However the eslint rule jsx-a11y/anchor-is-valid states:

The href attribute is required for an anchor to be keyboard accessible. Provide a valid, navigable address as the href value. If you cannot provide an href, but still need the element to resemble a link, use a button and change it with appropriate styles.

This seems to be in contravention to the w3c standard which the eslint rule actually links back to.

I suggest this eslint rule should be updated to allow anchor tags with no href as long as both role=link and aria-disabled=true are included.

@jessebeach
Copy link
Collaborator

jessebeach commented Nov 14, 2022

Well, this one has me thinking!

@asos-francesca, first question, what is the purpose of a disabled hyperlink in the case that you are trying to support? I'm trying to understand why a link would be disabled, like what that means semantically. Perhaps there is a good reason that the disabled attribute can't be applied to <a href> in HTML and the ARIA example is a case where it is possible to create a "disabled link", but that it creates a stilted user experience.

Second question, could you just use a <div> tag (or a <span> if you need inline layout styling)? You're applying a role to a tag, so the semantics of the original tag are replaced with that role. And an <a> tag and a <div> tag have the same underlying role generic. You're not using the <a> tag as a named anchor on the page. So functionally <div role="link" aria-disabled="true"> is going to behave the same as <a role="link" aria-disabled="true">. These examples are from https://codepen.io/jessebeach/pen/LYrLyxK

Screenshot 2022-11-13 at 11 14 28 PM

Screenshot 2022-11-13 at 11 15 35 PM

I'm not convinced that we need to support this edge case, but maybe I'm missing something that came up in the case you're trying to support.

@asos-francesca
Copy link
Author

asos-francesca commented Nov 14, 2022

@jessebeach thanks for the response!

We have a list of links that contains alternative colour options for a product. We want to express to the user that the links cannot be clicked because one colour is out of stock, but we still want to express to the user that alternative colour options are in existence, even if they are not available right now. For our visual frontend we display a greyed out image for these out of stock items, whereas the other items are in colour. The aria label explains this difference too with a label expressing colour and status, "Red. Out of Stock".

We thought a lot about the semantics of what to make this unclickable link. That was what lead us to the W3C suggestion of the instance of a placeholder link, it seemed to suggest that was the right pattern. There is clearly a convention for disabled links as expressed here: https://w3c.github.io/html-aria/#example-communicate-a-disabled-link-with-aria

That's why we went with this convention. The eslint ruling seems to state that you cannot have an without a href but the a11y guidelines across the web seem to suggest this is valid, as long as the aria-disabled=true is included. This is obviously a lot better than the previously common empty href (href="#") so I don't see a reason why the ruling couldn't be updated to check for the existence of an aria-disabled attribute and therefore approve. The current rule "The href attribute is required for an anchor to be keyboard accessible" is true, but my reading suggests not all links have to be keyboard accessible. In our case, this link is not focusable because it is not interactive, but it's still accessible via screenreader modifier keys, e.g in VO, which can read out the context of the link and why it's disabled. I believe on Mac this is expressed as the link being "dimmed" or "disabled".

https://a11y-guidelines.orange.com/en/articles/disable-elements/

@ljharb
Copy link
Member

ljharb commented Nov 14, 2022

Why wouldn't you want these "out of stock" links to still link to the product page for that color?

(If there's no per-color pages, then i'd suspect none of them should be links, and instead should be buttons)

@jessebeach
Copy link
Collaborator

Why wouldn't you want these "out of stock" links to still link to the product page for that color?

(If there's no per-color pages, then i'd suspect none of them should be links, and instead should be buttons)

Hmm, good point. If the the user is just picking a color, but not navigating to a new page, then the link is indeed the wrong role. When a link is activated, the user navigates to a place. So if the list of colors is mutually exclusive, then the best semantic concept is a radio group. If the list of colors is not mutually exclusive, then the best semantic concept is a button group with selected colors indicated with aria-pressed on the buttons that are active.

That does still leave us with how to handle the w3c recommendation for a disabled link, which is a problem that still intrigues me. I think that <a href> might be only case where a generic element is upgraded to an AX Tree node with the addition of a specific attribute. I'm still leaning toward recommending the solution to use a span or div:

<span role="link" aria-disabled="true"> in a case like the w3c example and it may even be worth putting in a PR to that page to make this change, so that folks aren't confused by the presence of <a>. It is the role="link" attribute that indicates to the browser that this should be represented as a link in the AX Tree, not the <a> element. So your code would probably look something like this (if it is React):

const ColorSwatch = isActive ? <a> : <span>;
return (
  <ColorSwatch
    href={isActive ? href : undefined}
    ...rest
  />
);  

It's not generally well known that many elements that seem to be semantic do not in fact have an AX Tree analog node. <a> does not have a ARIA role https://www.w3.org/TR/html-aam-1.0/#el-a-no-href mapping. I'm currently in the process of updating the mappings in #401.

@jessebeach
Copy link
Collaborator

By the way @asos-francesca this was a very good question. It got me thinking for a few days.

I think the conclusion here is to leave jsx-a11y/anchor-is-valid as it is, because it encourages the proper structure of a link. The only reason to use an <a> tag is to create a navigable link and that requires an href. If we loosen the recommendation here, we invite bad practices where we have a very positive signal that someone in trying to create a navigable link. You can get around the lint warning by using an element that isn't <a> if the behavior does not produce navigation, which is semantically preferred. The case of a disabled link represents a kind of anti-pattern (in my opinion, to be clear), that we want to discourage.

Let me know if you disagree. This is definitely an open discussion. You certainly discovered a debatable topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants