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

Inaccessible markdown heading links #8336

Closed
2 of 7 tasks
zmrhaljiri opened this issue Nov 14, 2022 · 9 comments · Fixed by #8562
Closed
2 of 7 tasks

Inaccessible markdown heading links #8336

zmrhaljiri opened this issue Nov 14, 2022 · 9 comments · Fixed by #8562
Labels
bug An error in the Docusaurus core causing instability or issues with its execution domain: a11y Related to accessibility concerns of the default theme

Comments

@zmrhaljiri
Copy link
Contributor

Have you read the Contributing Guidelines on issues?

Prerequisites

  • I'm using the latest version of Docusaurus.
  • I have tried the npm run clear or yarn clear command.
  • I have tried rm -rf node_modules yarn.lock package-lock.json and re-installing packages.
  • I have tried creating a repro with https://new.docusaurus.io.
  • I have read the console error message carefully (if applicable).

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

  • I'd be willing to fix this bug myself.
@zmrhaljiri zmrhaljiri added bug An error in the Docusaurus core causing instability or issues with its execution status: needs triage This issue has not been triaged by maintainers labels Nov 14, 2022
@slorber slorber removed the status: needs triage This issue has not been triaged by maintainers label Nov 16, 2022
@slorber
Copy link
Collaborator

slorber commented Nov 16, 2022

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?

@slorber slorber added the domain: a11y Related to accessibility concerns of the default theme label Nov 16, 2022
@zmrhaljiri
Copy link
Contributor Author

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
Copy link
Collaborator

slorber commented Nov 23, 2022

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
Copy link
Collaborator

slorber commented Nov 23, 2022

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
Copy link
Contributor Author

zmrhaljiri commented Nov 24, 2022

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
Copy link
Collaborator

slorber commented Nov 24, 2022

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
Copy link
Contributor Author

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

@zmrhaljiri
Copy link
Contributor Author

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

@slorber
Copy link
Collaborator

slorber commented Jan 18, 2023

You can submit a PR if you want 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug An error in the Docusaurus core causing instability or issues with its execution domain: a11y Related to accessibility concerns of the default theme
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants