starlight icon indicating copy to clipboard operation
starlight copied to clipboard

Accessibility audit with several findings (serious and best practise)

Open markteekman opened this issue 1 year ago • 6 comments

What version of starlight are you using?

Latest

What version of astro are you using?

Latest

What package manager are you using?

pnpm

What operating system are you using?

Mac

What browser are you using?

Chrome and Firefox

Describe the Bug

Accessibility audit

As promised a while ago here: https://github.com/withastro/starlight/issues/1950#issuecomment-2156391347 I spent a couple of hours to do a thorough audit of the Astro Starlight theme accessibility wise. The theme is really solid already, so this list consist mainly of findings to further improve the accessibility:

āš ļø Serious

  • Elements must meet minimum color contrast ratio thresholds.
    • Found on: https://starlight.astro.build/guides/authoring-content/ (in dark mode).
    • Comments in examples in dark mode just mis a tiny bit to meet the minimal requirement of 4.5:1. Although I would suggest to aim a little higher, 6:1 for example.
    • Screenshot: SchermĀ­afbeelding 2024-12-14 om 12 03 04
  • Heading structures should follow the numerical flow (h1 > h2 > h3 etc.)
    • Found on: https://starlight.astro.build/getting-started/
    • There is an h2 before the h1 on the page ("On this page" in the sidebar)
    • Screenshot: SchermĀ­afbeelding 2024-12-14 om 12 35 43
  • Interactive elements should have at least one or more visual indicators (besides color).
    • Found on: https://starlight.astro.build/.
    • The buttons and link in the hero have no hover/focus effect, consider for example:
      • Changing the underline on hover/focus.
      • Changing te background color on hover/focus.
      • Animating the icons on hover/focus.
      • Screenshot: SchermĀ­afbeelding 2024-12-14 om 12 47 32
  • Links opening in a new tab should announce it to the screen reader.
    • Found on: https://starlight.astro.build/.
    • Normally when a link opens in a new tab you would want to visually and textually represent that. Visually can be done through an icon and textually is normally done with an sr-only span that says "Opens in a new tab". On the homepage however there is a link with such an icon but it doesn't open in a new tab. Consider opening in a new tab or removing the icon so it's not potentially confusing.
    • Screenshot: SchermĀ­afbeelding 2024-12-14 om 12 50 55
  • Provide screen reader feedback when using "Copy to clipboard".
    • Found on: https://starlight.astro.build/getting-started/.
    • When using the screen reader and when copying code from a terminal example, a popup shows up with "Copied!" but this isn't communicated to the screen reader. Consider using an aria-live region for this.
    • Visual side note: there's a little gap between the arrow and the box.
    • Screenshot: SchermĀ­afbeelding 2024-12-14 om 14 05 14
  • Search bar.
    • Found on: https://starlight.astro.build.
    • When using the keyboard to navigate and pressing enter on "Load more results" the focus should be set on the first item of the newly loaded results. Instead, the focus is set back on the window forcing the user to tab back into the search and through all the previous results to get to the new results. Video: https://github.com/user-attachments/assets/e16f157f-fa12-45a5-a162-46fe342c1b32
    • Consider improving the "Load more results" button by saying "Load more results for {search input}".
    • When typing in a search query it would be benificial for screen reader users to hear what the search query gives back on results. So you visually see "10 results for npm" for example. This should be announced to the screen reader as well, using an aria-live region.
    • Consider improving the screen reader only text for the clear button from "Clear" to "Clear search input".
    • Consider using a role="combobox" for a better screen reader experience and provide arrow key up/down interaction on the results list for an improved keyboard experience. Example setup I made for another project:
<search>
  <form action="/search">
    <label for="search-field" class="sr-only">Search documentation</label>
    <div>
      <input
        type="text"
        autocomplete="off"
        placeholder="Search documentation"
        aria-controls="search-results-list"
        aria-autocomplete="list"
        aria-expanded="false"
        role="combobox"
      >
      <button type="button" aria-label="Clear search input">Ɨ</button>
      <div>
        <kbd>⌘</kbd>
        <kbd>K</kbd>
      </div>
      <button type="submit" aria-label="Search">šŸ”</button>
    </div>
    <div hidden>
      <div>
        <p>No results found</p>
      </div>
      <ul id="search-results-list" role="listbox"></ul>
      <div>
        <div>
          <kbd>Enter</kbd> <span>to select</span>
        </div>
        <div>
          <kbd>↓</kbd> <kbd>↑</kbd> <span>to navigate</span>
        </div>
        <div>
          <kbd>Esc</kbd> <span>to close</span>
        </div>
      </div>
    </div>
  </form>
</search>
  • When on a resolution of 1280x1024 and zoomed in 400%, the fixed elements on the page block a lot of the pages content.
    • Found on: https://starlight.astro.build/getting-started/.
    • Consider maybe hiding the main menu when scrolling down and showing it when scrolling up.
    • Screenshot: SchermĀ­afbeelding 2024-12-14 om 15 32 20

♿ Best practices

  • Tabs.
    • Found on: https://starlight.astro.build/guides/authoring-content/.
    • Use button instead of a for tab buttons.
    • Further improve relationship by using aria-controls="tabpanel-id" on the tab button.
    • Consider moving the focus to the first item when the focus is on the last item and the user presses the left arrow key and vice versa.
    • Example: https://www.w3.org/WAI/ARIA/apg/patterns/tabs/examples/tabs-automatic/.
    • Astro example: https://accessible-astro.netlify.app/accessible-components/ (down the page).
    • Screenshot: SchermĀ­afbeelding 2024-12-14 om 12 24 58
  • Header social icons.
    • Found on: https://starlight.astro.build/.
    • Consider improving screen reader text to "Visit GitHub repository" and "Open Discord server" respectively.
    • Screenshot: SchermĀ­afbeelding 2024-12-14 om 12 43 46
  • Text styled as heading not marked up as heading.
    • Found on: https://starlight.astro.build/.
    • Text that is made to look like a heading benefits from being marked up as a heading as well, it's also better for SEO. The features on the homepage would benefit if the span is replaced with an h2.
    • Screenshot: SchermĀ­afbeelding 2024-12-14 om 12 52 36
  • Distinct landmarks using aria-label.
    • Found on: https://starlight.astro.build/getting-started/.
    • To help screen reader users navigate, label your landmarks if there are more then one of the same on the page, such as for the right sidebar, which could be <aside aria-label="On page navigation"> for example.
    • Screenshot: SchermĀ­afbeelding 2024-12-14 om 14 19 41
  • General.
    • Don't use color alone to provide visual cues for links and other interactive elements. Many links use only color to indicate interactivity, which might not be enough for visually impaired users.
    • Consider using extra visual cues such as underlines or animating an icon that's associated with a link. For links that already have an underline you can remove the underline. You can also give underlines visual appeal / transitioning effects using text-underline-offset and text-decoration-thickness for example.
    • The same is true for the link in the banner: "Updating to Astro 5? Learn how to upgrade".
      • Examples: SchermĀ­afbeelding 2024-12-14 om 14 12 29 SchermĀ­afbeelding 2024-12-14 om 14 12 51
    • Consider not relying on the browsers focus outline but instead provide one to be consistent across browsers and color schemes. For example, here's the difference between Firefox and Chrome:
      • Firefox: SchermĀ­afbeelding 2024-12-14 om 15 19 23
      • Chrome: SchermĀ­afbeelding 2024-12-14 om 15 19 32
    • Using something like the code below gives a consistent focus outline for interactive elements:
@mixin outline {
 outline: 2px dotted black;
 outline-color: black;
 outline-offset: 0;
 -webkit-box-shadow: 0 0 0 2px white;
 box-shadow: 0 0 0 2px white;
}

*:focus,
*:focus-visible {
 @include outline;
}

*:focus:not(:focus-visible) {
 outline: none;
 box-shadow: none;
}
  • Consider improving the logo's sr-only to "Starlight logo - Go to homepage".
  • Consider adding an accessibility statement: https://www.w3.org/WAI/planning/statements/.

Also, I don't want this list to come over like a complaint, because Starlight is really, really well made accessibility wise! These are just some of my findings and a deep dive audit of how I normally audit projects at my work. Let me know if these will be taken in consideration, I'll be happy to assist by submitting a PR, especially since I'll be using the theme in the near future and need it as accessible as possible šŸ™‚

Link to Minimal Reproducible Example

https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics?file=README.md

Participation

  • [X] I am willing to submit a pull request for this issue.

markteekman avatar Dec 14 '24 15:12 markteekman

Thank you @markteekman! Appreciate you taking the time to share these.

Would you mind also sharing some resources around how you determined what is serious, what is a best practice, etc.? There are some of these where I don’t necessarily agree with the analysis/recommendation, so I’m curious to learn more about the reasoning behind them. For example, are there specific WCAG success criteria that you feel are not met? If so, which ones? That kind of thing would be super helpful.

delucis avatar Dec 16 '24 19:12 delucis

Quick responses to the ā€œseriousā€ items:

  • Elements must meet minimum color contrast ratio thresholds.

Definitely. Should be fixed by #2708.

  • Heading structures should follow the numerical flow (h1 > h2 > h3 etc.)

This recommendation usually applies to the page’s main content. Use of heading levels that don’t start at h1 (such as the h2 you highlighted) is allowable outside of the primary content.

  • Interactive elements should have at least one or more visual indicators (besides color).

These buttons have a clear visual indication from their context and the background colour on the ā€œGet startedā€ link. Hover/focus styles would be nice but not an accessibility fix as they don’t help users who can’t hover/focus.

  • Links opening in a new tab should announce it to the screen reader.

That link doesn’t open in a new tab.

  • Provide screen reader feedback when using "Copy to clipboard".

Agreed. This will need fixing upstream in Expressive Code. I’ll try to find time to make a PR there — the fix shouldn’t be too hard I don’t think.

  • Search bar.

I’d definitely love to improve things here. On top of the items you highlighted, basic QoL keyboard navigation is also not that great.

The current implementation is the default UI provided by the Pagefind library. In all likelihood, we need to build our own UI entirely from scratch to get everything we need. It’s something @HiDeoo and I have discussed and want to get to eventually, but obviously it’s also a pretty major undertaking.

  • When on a resolution of 1280x1024 and zoomed in 400%, the fixed elements on the page block a lot of the pages content.

This is a known issue, reported in #316.

delucis avatar Dec 16 '24 21:12 delucis

Hey Chris, my apologies, I should have provided more context to why I marked something as serious vs best practice. To be honest, I tend to mark more findings as serious which is purely subjective because I believe in building the most accessible experience possible, not just meeting minimum requirements. While complying with WCAG success criteria is important, we can often improve the UX for people with disabilities even further by implementing additional best practices. With that said, let me distill this list further, as most items should actually be categorized as best practices šŸ™‚

Also, I removed the point about headings. I learned something new here - I didn't realize heading hierarchy was specific to landmarks! I was misled by an outdated tool I use (HeadingsMap) that flags these as errors. So thanks for that šŸ˜„

Anyway, here's a more accurate list of the findings:

āš ļø Serious

Elements must meet minimum color contrast ratio thresholds

  • Found in: Dark mode code comments
  • Required: Yes
  • Success criteria: 1.4.3 Contrast (Minimum)
  • Note: The contrast ratio must be at least 4.5:1 for regular text (like you mentioned that should be fixed in #2708)

Fixed elements blocking content at 400% zoom

  • Found in: Navigation at 1280x1024 resolution
  • Required: Yes
  • Success criteria: 1.4.10 Reflow
  • Note: Content must be readable and functional when zoomed to 400% (as you've mentioned reported in #316)

♿ Best Practices

Interactive elements visual indicators

  • Found in: Hero buttons and links
  • Required: No, but recommended
  • Related success criteria: 1.4.1 Use of Color
  • Note: While not strictly required, visual indicators improve usability

Links opening in new tab announcement

  • For this one I actually know it doesn't open in a new tab, but the visual icon used suggests it does which can (but doesn't have to be) confusing for some people as to what they're expecting.

Copy to clipboard feedback

  • Found in: Code block copy buttons
  • Required: No, but recommended
  • Related success criteria: 4.1.3 Status Messages
  • Note: Status updates improve screen reader user experience

Search bar improvements

  • Found in: Global search functionality
  • Required: No, but recommended
  • Related success criteria: 2.4.3 Focus Order
  • Note: Suggested improvements for better keyboard and screen reader experience

Tabs implementation

  • Found in: Content tabs
  • Required: No, but recommended
  • Related success criteria: 4.1.2 Name, Role, Value
  • Note: Following ARIA patterns improves usability

Header social icons improved labeling

  • Found in: Header navigation
  • Required: No, but recommended
  • Related success criteria: 2.4.6 Headings and Labels
  • Note: More descriptive labels improve understanding

Text styled as heading markup

  • Found in: Homepage features section
  • Required: No, but recommended
  • Related success criteria: 1.3.1 Info and Relationships
  • Note: Semantic markup improves document structure

Distinct landmarks using aria-label

  • Found in: Page navigation areas
  • Required: No, but recommended
  • Related success criteria: 1.3.1 Info and Relationships
  • Note: Labeled landmarks improve navigation

General improvements

  • Found in: Throughout the site
  • Required: No, but recommended
  • Related success criteria: Multiple
  • Note: Includes focus styles, underlines, and consistent visual indicators

markteekman avatar Dec 17 '24 08:12 markteekman

Thanks for the extra links — that is super helpful! Will make some time to look at these in more detail.

delucis avatar Dec 17 '24 11:12 delucis

Wow! I just read through this really awesome summary and analysis of a11y of the whole Starlight project basically! I'm not at all an expert in this, so I just wanted to leave a short comment that empathizes how much I appreciate such a detailed and thorough investigation ā¤

trueberryless avatar Feb 28 '25 19:02 trueberryless

Thanks for the kind words @trueberryless, that really means a lot 😊

markteekman avatar Mar 01 '25 11:03 markteekman

Normally when a link opens in a new tab you would want to visually and textually represent that. Visually can be done through an icon and textually is normally done with an sr-only span that says "Opens in a new tab". On the homepage however there is a link with such an icon but it doesn't open in a new tab.

That icon typically indicates that the hyperlink will take you outside the current site or application. On Wikipedia, it’s used to show that the link leads to a page that is not part of Wikipedia. It does not indicate that it will open in a new tab.

Aloso avatar Nov 26 '25 11:11 Aloso

Normally when a link opens in a new tab you would want to visually and textually represent that. Visually can be done through an icon and textually is normally done with an sr-only span that says "Opens in a new tab". On the homepage however there is a link with such an icon but it doesn't open in a new tab.

That icon typically indicates that the hyperlink will take you outside the current site or application. On Wikipedia, it’s used to show that the link leads to a page that is not part of Wikipedia. It does not indicate that it will open in a new tab.

Yes, that's true. I've heard many different opinions about it, and I agree that an icon can indicate navigation to a different domain, but the link doesn't need to open in a new tab. If you do open it in a new tab, the sr-only span should apply. The indicator is optional and can be omitted entirely if you follow the approach stated by Gov.uk: https://designnotes.blog.gov.uk/2016/11/28/removing-the-external-link-icon-from-gov-uk/

markteekman avatar Nov 28 '25 12:11 markteekman

Thanks again for this issue @markteekman! Sorry it’s taken long to go through it all — it’s a lot of stuff all in one place šŸ˜…

I think for the future it would be good to break up things like this into small single-issue posts to tackle because it can be hard to track everything in an omnibus issue like this. As we’ve seen, some of these need discussing and we end up trying to discuss all kinds of things at once making it hard to track discussion of any specific point.

Which… is exactly what I’m about to try to do šŸ˜€

TL;DR I’m going to close this as a lot of the biggest items here are either fixed or tracked elsewhere. I’ve tried sharing details for everything below. If I missed something, my apologies — I’d welcome new issues for anything people spot (one issue per bug) and I’d definitely welcome a new discussion to plan creating an accessibility statement, which I’d love for us to do.


Serious

Elements must meet minimum color contrast ratio thresholds.

This was fixed in #2708 (released in Starlight v0.31.0)

Fixed elements blocking content at 400% zoom

We still need to figure out a good solution for this, but it is already tracked in #316

Best practices

Code block copy to clipboard button

This still needs fixing, I lost track of this one sorry, but it’s an upstream issue, so we should track it in the Expressive Code repo: https://github.com/expressive-code/expressive-code/issues/368

Search bar improvements

Work on this is underway in #3335

Tabs

Use button instead of a for tab buttons.

I disagree with this suggestion unless we have evidence from users that it would have an impact. The role="tab" attribute already overrides link semantics, and using a link makes this a progressive enhancement: if JavaScript fails for whatever reason, the relationship between tabs and tab panels is preserved.

Further improve relationship by using aria-controls="tabpanel-id" on the tab button.

Do we have an example of what this would improve for users? I’m open to this, but would be good to understand the user impact rather than adding attributes just in case they help.

Consider moving the focus to the first item when the focus is on the last item and the user presses the left arrow key and vice versa.

I’m torn on this one. I know it’s a common pattern (for example on sets of radio buttons), but I don’t know if it’s more confusing to never get to the ā€œendā€ of a group. I’d be interested to hear feedback from users who actually dislike the current behaviour.

Header social links

Since v0.33.0, Starlight is no longer responsible for the social links labelling, instead this is set by users in their Starlight config, so I think we can consider this one resolved.

Card headings not marked up as heading

This would indeed be nice to markup like you say. Unfortunately it’s not an easy thing to do: users can put a <Card> component anywhere and we don’t know where in the heading hierarchy they are to know which heading level to use.

On balance, I think it’s OK to treat cards less as content with a heading and more like a styled list item. Marking up as a heading would help for stuff like navigating directly to a card, but unlike page sections, cards are only small snippets of content, so I think it’s OK to have people navigate to a preceding subheading and then scan through card by card.

Labelling distinct landmarks

Agree with this suggestion, would welcome a PR to label any key landmarks that are currently missing labels!

Link styling

I think I’d need to see an issue with more examples of the problem/suggestions of how to fix. AFAIK we already style links in a distinct way, but happy to be proven wrong.

delucis avatar Dec 02 '25 11:12 delucis

@delucis totally agree with you! These should have been separate issues, or even better, just a discussion to begin with šŸ™‚ And no worries, I could have invested more time in this after doing the investigation, but I'm very busy with the Accessible Astro projects in the limited time I have šŸ¤” It's great though to see that a lot has happened already! Here's the answers to some of your concerns.

.. and I’d definitely welcome a new discussion to plan creating an accessibility statement, which I’d love for us to do.

Done! 😊

I disagree with this suggestion unless we have evidence from users that it would have an impact. The role="tab" attribute already overrides link semantics, and using a link makes this a progressive enhancement: if JavaScript fails for whatever reason, the relationship between tabs and tab panels is preserved.

I hear you on this one, and it's a valid point! I'm always very pragmatic when testing things, so for this I went with the advice simply because it follows the W3C ARIA APG exactly. It aligns with the reference pattern and should be recognizable for developers working on it. That said, the progressive enhancement is a valid point šŸ‘ŒšŸ¼ Just make sure the content is accessible through CSS when JS might fail.

Do we have an example of what this would improve for users? I’m open to this, but would be good to understand the user impact rather than adding attributes just in case they help.

Good question. Actually, the screen reader support is quite poor for this, but the reason I mentioned it is for a couple of reasons: it's semantically correct and follows the reference pattern, future screen readers might improve support and it helps automated testing tools understand relationships. So not a game-changer per se, but it doesn't harm as well.

I’m torn on this one. I know it’s a common pattern (for example on sets of radio buttons), but I don’t know if it’s more confusing to never get to the ā€œendā€ of a group. I’d be interested to hear feedback from users who actually dislike the current behaviour.

Reasons for this one is similar to the others: it's in the W3C ARIA APG reference implementation and it matches the users expectation for things like radio buttons and other desktop related patterns such as CMD/CTRL+Tab. And when you have a lot of tabs, it's most often more useful to circle back then having to navigate all the way back.

This would indeed be nice to markup like you say. Unfortunately it’s not an easy thing to do: users can put a <Card> component anywhere and we don’t know where in the heading hierarchy they are to know which heading level to use.

On balance, I think it’s OK to treat cards less as content with a heading and more like a styled list item. Marking up as a heading would help for stuff like navigating directly to a card, but unlike page sections, cards are only small snippets of content, so I think it’s OK to have people navigate to a preceding subheading and then scan through card by card.

Hmmm, yeah that's a though one. One way would be to allow for a prop so the user can set the correct heading level. That would put the responsibility with the user though and it's no guarantee that it would work. I did the same for our Card, defaulting to an h2. If the Cards are truly presentational, it okay to leave them as is, otherwise I would consider adding such an option (but I get that there's a lot of gotchas with user responsibility and all).

Agree with this suggestion, would welcome a PR to label any key landmarks that are currently missing labels!

I'll see to this one! 😊

I think I’d need to see an issue with more examples of the problem/suggestions of how to fix. AFAIK we already style links in a distinct way, but happy to be proven wrong.

I'll have to dive into this one a little deeper, I'll let you know.

Thanks again for the time and effort you guys put into this Chris!

markteekman avatar Dec 07 '25 18:12 markteekman

Just adding a note for the record that the latest Expressive Code release (0.41.4) fixes the copy button issue mentioned above (see https://github.com/expressive-code/expressive-code/pull/369 for implementation).

Starlight already use v0.41.x of Expressive Code so new installs should get this improvement automatically.

delucis avatar Dec 11 '25 11:12 delucis

@delucis checked the landmarks in VoiceOver, they’re already good as is! šŸ‘šŸ¼

I also double-checked the link styling. It’s correct: underline plus color changes for main content, and color-only changes in <nav> areas.

markteekman avatar Dec 13 '25 10:12 markteekman