Accessibility audit with several findings (serious and best practise)
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:
- Heading structures should follow the numerical flow (h1 > h2 > h3 etc.)
- Found on: https://starlight.astro.build/getting-started/
- There is an
h2before theh1on the page ("On this page" in the sidebar) - Screenshot:
- 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:
- 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:
- 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-liveregion for this. - Visual side note: there's a little gap between the arrow and the box.
- Screenshot:
- 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-liveregion. - 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:
āæ Best practices
- Tabs.
- Found on: https://starlight.astro.build/guides/authoring-content/.
- Use
buttoninstead ofafor 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:
- Header social icons.
- Found on: https://starlight.astro.build/.
- Consider improving screen reader text to "Visit GitHub repository" and "Open Discord server" respectively.
- Screenshot:
- 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
spanis replaced with anh2. - Screenshot:
- 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:
- 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-offsetandtext-decoration-thicknessfor example. - The same is true for the link in the banner: "Updating to Astro 5? Learn how to upgrade".
- Examples:
- Examples:
- 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:
- Chrome:
- Firefox:
- 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.
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.
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.
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
Thanks for the extra links ā that is super helpful! Will make some time to look at these in more detail.
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 ā¤
Thanks for the kind words @trueberryless, that really means a lot š
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.
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/
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
buttoninstead ofafor 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 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!
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 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.