facebook / docusaurus

Easy to maintain open source documentation websites.
https://docusaurus.io
MIT License
55.56k stars 8.33k forks source link

Inaccessible markdown heading links #8336

Closed zmrhaljiri closed 1 year ago

zmrhaljiri commented 1 year ago

Have you read the Contributing Guidelines on issues?

Prerequisites

Description

Heading hash links in Markdown docs are missing the accessible name. Screen reader users who tab through the page by keyboard and focus on the links hear only "Direct link to heading" (the text inside the title attribute of hash links).

Also, when users of screen readers populate a list of links on the page, it is showing the following:

nvda-link-list-docusaurus

Basically, they do not know where the links will take them since there is no meaningful text content.

Reproducible demo

https://docusaurus.io/docs#fast-track

Steps to reproduce

  1. Go to any docusaurus docs page, for example, https://docusaurus.io/docs
  2. Turn on the screen reader (e.g. NVDA on Windows, or VoiceOver on Mac)
  3. Tab through the heading links:
    • Fast Track (the first hash link)
    • Docusaurus: Documentation Made Easy (the second hash link)
    • and so on
  4. When focused on the link, the screen reader announces something like "Direct link to heading, link"

Expected behavior

The hash links should have an accessible name, ideally the heading content, so screen reader users know where the links are pointing to.

The suggestion for the new HTML structure of a sample link:

<h2 id="fast-track">
  <a href="#fast-track" title="Direct link to Fast track">Fast track</a>
</h2>

The implementation of the # character can be still made on hover, similarly as on MDN.

Actual behavior

The hash links have no accessible name (no unique text content) so screen reader users do not know where the links are pointing to.

The current HTML structure of a sample link:

<h2 id="fast-track">
  Fast Track 
  <a class="hash-link" href="#fast-track" title="Direct link to heading">&ZeroWidthSpace;</a>
</h2>

Your environment

Self-service

slorber commented 1 year ago

Makes sense, thanks for reporting.

The hash links should have an accessible name, ideally the heading content,

The implementation of the # character can be still made on hover, similarly as on MDN.

Is this mandatory or an official a11y reco?

Many other solutions are not adding the link on the full heading, see for example:

Personally, I don't find the MDN implementation pleasant to look at for a regular user without any disability, and would prefer to keep the existing pattern and just fix the title, is it fine?


Note we already have a translated anchor title:

<a
        className="hash-link"
        href={`#${id}`}
        title={translate({
          id: 'theme.common.headingLinkTitle',
          message: 'Direct link to heading',
          description: 'Title for link to heading',
        })}>
        &#8203;
</a>

Do you have a recommendation for the label so that we do not have to "rewrite" all the existing translations from scratch? (I personally can only translate EN + FR)

Is something like "Direct link to heading - {headingLabel}" good enough?

zmrhaljiri commented 1 year ago

Thank you for your reply!

IMHO it would be a violation of the WCAG success criterion 2.4.4. Link Purpose (In Context) (A), however, the user experience for users of assistive technologies is currently as described, so any approach that results in links having an accessible and meaningful name would be fine. There is a good article about this problematic: https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/.

The text you're suggesting for the title is great. The main problem is we cannot use the title attribute as the only means for setting the accessible name of the link as it is infamously inaccessible (not supported by many screen readers). You can supply the helper text "Direct link to heading - {headingLabel}" into the title, but the link should have the text content (instead of &#8203;) in the first place.

slorber commented 1 year ago

What I see in practice is that tools like VoiceOver will announce better if the link has aria-label rather than using title. Isn't this a good-enough solution?

Duplicating the link title in a hidden element feels like a weird solution to me πŸ€·β€β™‚οΈ

CleanShot 2022-11-23 at 18 47 38@2x

Will read that carefully when I have time but if anyone can suggest a good-enough solution that does not involve useless hidden child elements duplication + making the whole text the anchor link, I'd be happy to implement it fast.

slorber commented 1 year ago

Can you tell exactly how we violate the spec according to you?

Unfortunately I don't have much time to read in details the whole docs πŸ˜…

zmrhaljiri commented 1 year ago

I didn't suggest duplicating the link title in a hidden element - the approach I suggested is to wrap the link inside the heading to have just one text, optionally with also the title attribute serving as only a helper text for sighted, non-mobile users:

<h2 id="fast-track">
  <a href="#fast-track" title="Direct link to Fast track">Fast track</a>
</h2>

This way you can avoid extra elements and the screen reader announces something like "Heading level two, Fast track, link". The downside of this approach is some users might be confused about heading links.

Another approach can be the aria-label way you suggested, which would also work since the accessible name of the link would not be empty space or "#" character, but "Direct link to Fast track":

<h2 id="fast-track">
  Fast Track 
  <a class="hash-link" href="#fast-track" aria-label="Direct link to Fast track">#</a>
</h2>

Either of these approaches should be fine.

Regarding the specs, in short, they say: "The purpose of each link can be determined from the link text or from the link text together with its programmatically determined link context". While one can argue if the text inside of the heading is a programmatically determined context for the link or not, it's always the best practice for links to have a unique text because of user experience. That's why links with text like "click here" or "read more" are not considered best practice, because users then need to scan the surrounding content to learn about the link context. At the moment, the text of all hash links is just "#".


Sorry, I missed your points about the other solutions before:

Many other solutions are not adding the link on the full heading, see for example:

https://squidfunk.github.io/mkdocs-material/getting-started/ https://vitepress.vuejs.org/guide/what-is-vitepress

On https://squidfunk.github.io/mkdocs-material/getting-started/, the HTML structure is:

<h2 id="installation">
  Installation
  <a class="headerlink" href="#installation" title="Permanent link">ΒΆ</a>
</h2>

The issue here is the link does not have a unique accessible name. Screen readers will try to read the "ΒΆ" character, and a few of them, at best, will read only "Permanent link" (but permanent link to what?), because as previously mentioned, title attribute should not be used to define the accessible name. So this is practically the same issue as currently on Docusaurus.

On https://vitepress.vuejs.org/guide/what-is-vitepress, the HTML structure is:

<h1 id="what-is-vitepress" tabindex="-1">
  What is VitePress?
  <a class="header-anchor" href="#what-is-vitepress" aria-hidden="true">#</a>
</h1>

There are different issues. First of all, tabindex="-1" on <h1> has no effect as headings are not focusable elements. Second, they put aria-hidden="true" on <a>, which hides the link from the accessibility tree, but because the anchor is a focusable element, you can still focus it with the keyboard, so screen readers are announcing just "blank". There is a specific axe rule describing it: https://dequeuniversity.com/rules/axe/4.4/aria-hidden-focus. If they would put tabindex="-1" on the anchor, then together with aria-hidden="true" it would hide it from screen readers completely, which I think they tried to achieve, and it might seem as a good fix, but accessibility is not about removing or hiding functionality from AT users, but to provide an equivalent experience.

Thank you for taking the time looking to this, really. I realize it can be quite tricky and complex. I am currently building a site dedicated to web accessibility using Docusaurus, and I'd greatly appreciate if accessibility issues and difficulties can be handled, or optionally have control over the markup so I can adjust it.

slorber commented 1 year ago

Thanks for the explanations!

Another approach can be the aria-label way you suggested, which would also work since the accessible name of the link would not be empty space or "#" character, but "Direct link to Fast track":

I guess we can move on and just add this new aria-label then, that looks like a good enough solution

Sorry, I missed your points about the other solutions before

Not saying those linked examples are good accessibility examples, just wanted to illustrate other docs sites preferring not having clickable anchor headings.

Thank you for taking the time looking to this, really. I realize it can be quite tricky and complex. I am currently building a site dedicated to web accessibility using Docusaurus, and I'd greatly appreciate if accessibility issues and difficulties can be handled

We also appreciate having a base of users caring about accessibility, as this helps up improve on topics we may not have expertise on πŸ˜„

optionally have control over the markup so I can adjust it.

In any case, you should be able to swizzle headings components and implement your own markup, but we should have good accessibility by default.

If aria-label is not good enough for you, you can swizzle and make it use heading anchor links instead.

zmrhaljiri commented 1 year ago

Thanks a lot, much appreciated! If aria-label will be there, no need for me to swizzle then :) πŸ’š

zmrhaljiri commented 1 year ago

Hi @slorber! Is this something the community can fix or you would like to work on it?

slorber commented 1 year ago

You can submit a PR if you want πŸ™Œ