Docusaurus accessibility: common WCAG 2.2 issues
Docusaurus gives you a reasonably accessible starting point, and most sites then lose some of it. The losses rarely come from the framework itself. They come from custom colors, custom components, a sticky banner someone added for a launch, and years of Markdown written without anyone checking alt text or heading order. This post lists the WCAG 2.2 AA issues we look for first on a Docusaurus site, where each one usually comes from, and how to test for them.
The success criterion numbers and names below are from WCAG 2.2. If you sell to enterprise or public-sector customers in the US, you have probably been asked for an accessibility conformance report, usually built on the VPAT template, and these are the criteria that report will be judged against.
What Docusaurus gives you
It is worth knowing the baseline, so you do not rebuild what is already there or break it by accident. In the classic theme:
- A skip to main content link is included, so keyboard users can bypass the navbar.
- The page's
langattribute is set from your locale configuration, which matters for screen reader pronunciation and for WCAG 3.1.1 Language of Page. - The built-in Tabs component uses tab roles, and the built-in collapsible
detailselement is keyboard operable. - Heading anchors are offset for the sticky navbar, so jumping to a section does not hide the heading underneath it.
Swizzling components, replacing the theme's CSS or adding interactive MDX components can undo any of this. The issues below are the ones we most often find after that has happened.
Issues that come from customization
Contrast in custom palettes and dark mode
1.4.3 Contrast (Minimum) and 1.4.11 Non-text Contrast. Teams set a brand primary color, Infima derives its shades, and nobody checks the result in both color modes. The usual failures are link text on tinted admonition backgrounds, muted gray text in the footer, code syntax colors in whichever theme was picked for dark mode, and focus rings or form borders that fall below 3:1. Check every color pairing in light and dark mode, not just the body text.
Focus indicators removed
2.4.7 Focus Visible. A CSS reset or a designer's request removes outlines with outline: none, and keyboard users lose track of where they are. Restore a visible indicator that meets non-text contrast against its background:
:focus-visible {
outline: 2px solid var(--ifm-color-primary-darker);
outline-offset: 2px;
}
Pick a color that reaches 3:1 against the page background in both modes; the primary color does not always.
Focus hidden under the sticky navbar or a banner
2.4.11 Focus Not Obscured (Minimum), new in WCAG 2.2. When a keyboard user tabs backward up a page, the browser scrolls the focused link into view, and a sticky navbar, announcement bar or cookie banner can cover it entirely. Docusaurus offsets heading anchors, but not every focusable element. Scroll padding on the root element fixes most cases:
html {
scroll-padding-top: calc(var(--ifm-navbar-height) + 1rem);
}
If you add an announcement bar or a cookie banner fixed to the bottom of the viewport, add matching padding for it, and test by tabbing through a long page in both directions.
Small targets
2.5.8 Target Size (Minimum), also new in 2.2. Interactive targets need to be at least 24 by 24 CSS pixels, or spaced so that a 24-pixel circle around each does not overlap its neighbors. Links inside running text are exempt. The places we find failures are icon-only navbar items, social icons in the footer, custom pagination and small buttons in custom components. Measure them in the browser's developer tools rather than guessing.
Custom interactive components
2.1.1 Keyboard and 4.1.2 Name, Role, Value. An accordion built from a div with a click handler, a custom tab set, a copy button without an accessible name, or a filter widget that only responds to the mouse. Prefer the built-in Tabs and details where they fit. When you do build a component, use real button elements, expose state with attributes such as aria-expanded, and test with the keyboard alone.
Motion
2.2.2 Pause, Stop, Hide. Autoplaying animations, looping GIFs and animated illustrations that run longer than five seconds need a way to pause them. Respecting the prefers-reduced-motion media query is good practice on top of that, even though the criterion that requires it is AAA.
Issues that come from content
Images without text alternatives
1.1.1 Non-text Content. Markdown makes alt text easy to skip:  builds without complaint. Every informative image needs alt text that says what the image communicates, decorative images need empty alt text, and diagrams need a text equivalent nearby. Screenshots of text, such as error messages or config files, should be replaced with the text itself, which also helps search and support bots.
Heading structure
1.3.1 Info and Relationships and 2.4.6 Headings and Labels. In Docusaurus, the title in front matter is the page's h1, so body content should start at h2. Common problems are a second h1 in the body, headings chosen for their size rather than their level, and bold paragraphs used as fake headings. Screen reader users navigate by headings; a broken outline is a broken table of contents.
Link text
2.4.4 Link Purpose (In Context). "Click here", "this page" and bare URLs tell a screen reader user nothing when links are listed out of context. Write link text that names the destination.
Video and multilingual snippets
1.2.2 Captions (Prerecorded) applies to every embedded product video, including the short ones in release announcements. And on multilingual pages, a passage in another language needs its own lang attribute under 3.1.2 Language of Parts.
Help in a consistent place
3.2.6 Consistent Help, new in 2.2 at Level A. If your help center offers a contact link, chat widget or support form on multiple pages, it should appear in the same relative order each time. Custom page layouts and one-off landing pages are where this drifts.
How to test
No single tool covers WCAG 2.2. We use three layers.
Automated checks in CI. Build the site, serve it, and run pa11y-ci against the sitemap, with both axe and HTML_CodeSniffer as runners:
{
"defaults": {
"standard": "WCAG2AA",
"runners": ["axe", "htmlcs"],
"timeout": 30000
}
}
npm run build && npx docusaurus serve --port 3000 &
npx pa11y-ci --sitemap http://localhost:3000/sitemap.xml \
--sitemap-find https://docs.example.com --sitemap-replace http://localhost:3000
Automated tools reliably catch contrast failures, missing alt attributes, missing form labels and some ARIA misuse. They cannot tell you whether alt text is meaningful, whether focus order makes sense or whether focus is obscured. Add the check to your pipeline as a review gate; the handbook page on review gates covers where it fits.
A manual keyboard pass. Unplug the mouse. On a representative page of each template, tab through everything forward and backward, open and close every interactive component, use the search, switch color mode, and check that focus is always visible and never hidden.
A screen reader smoke test. VoiceOver on macOS or NVDA on Windows, on the same pages: listen to the heading outline, the link list and a few images. Also zoom the browser to 400 percent and check that pages reflow without horizontal scrolling of the page itself, under 1.4.10 Reflow; code blocks and wide data tables may scroll on their own.
Build it in, not on
The cheapest time to fix these is before they reach the site: a component checklist when someone builds an MDX component, alt text and heading order in the pull request template, and the automated check in CI. Our post on docs as code without the cargo cult covers which pipeline checks pay for themselves, and the migration checklist puts an accessibility pass in the QA phase for exactly this reason.
If you want an outside review, our docs accessibility audit covers a Docusaurus site against WCAG 2.2 AA from $1,900, with each finding tied to a criterion and a fix, and Docusaurus engineering covers implementing the fixes. Send us your site URL through the contact page. We reply within 1 business day.