Docusaurus Accessibility Checklist 2026 | WCAG 2.1 AA & EAA Compliance
Last updated: 2026-05-02
Docusaurus is the documentation framework Meta built and open-sourced, and it now powers documentation for tens of thousands of open-source projects and commercial SDK products including Algolia, Babel, Redux, and Jest. The default theme is React-based, supports MDX content, ships with Algolia DocSearch, and includes versioning, internationalization, and a built-in sidebar generator. While the maintainers have invested in accessibility, the framework's flexibility means real-world Docusaurus sites frequently regress on WCAG 2.1 AA: custom Markdown themes break code-block contrast, MDX components inserted by docs writers introduce heading hierarchy bugs, the version dropdown is rarely keyboard-tested, and the integrated search dialog often lacks a proper accessible name. With the European Accessibility Act enforceable since 28 June 2025 against any business that sells digital products into the EU and a growing pattern of plaintiff law firms targeting SDK and API documentation as part of broader product compliance complaints, engineering teams that ship docs alongside paid SaaS products are in scope. Docusaurus docs are also the primary developer-facing surface for many enterprise sales motions, which means an inaccessible docs site is a commercial risk independent of legal exposure. This checklist is written for the technical writer and the framework maintainer working together: writer-facing tasks they can complete inside MDX files, and template-level fixes the docs maintainer can ship in the swizzled theme components. Each issue cites the specific WCAG 2.1 success criterion that applies and includes the file path you would edit in a typical Docusaurus 3 project.
Common Accessibility Issues
Default Code Block Theme (Palenight) Fails 4.5:1 Contrast on Comments and Strings
WCAG 1.4.3Docusaurus 3 ships with the Palenight syntax-highlighting theme by default. Palenight uses muted purple comments (#697098) on a near-black background (#292d3e) that measure roughly 3.6:1, below the WCAG 1.4.3 minimum of 4.5:1 for normal text. Code is content in documentation, and developers regularly link directly to specific code examples, so unreadable code blocks block comprehension.
In docusaurus.config.js, change the prism theme to one with verified contrast. Replace require('prism-react-renderer').themes.palenight with require('prism-react-renderer').themes.vsDark or oneDark, both of which pass 4.5:1 across all token types. For light mode, set lightTheme to github or oneLight. Verify after the change with axe DevTools or a manual contrast check on a few code samples that include comments, strings, and keywords.
// docusaurus.config.js
themeConfig: {
prism: {
theme: require('prism-react-renderer').themes.palenight,
darkTheme: require('prism-react-renderer').themes.dracula,
},
}, // docusaurus.config.js
themeConfig: {
prism: {
theme: require('prism-react-renderer').themes.github,
darkTheme: require('prism-react-renderer').themes.vsDark,
},
}, The default Docusaurus sidebar uses a button element to toggle categories open or closed. In some swizzled themes, the button receives focus on click but the focus is not released back to the sidebar after the user activates a child link, leaving keyboard users unable to tab back up to the toggle. The bug is amplified when the docs site uses the autoCollapseCategories option which closes other categories on toggle, sometimes resetting focus to the page top.
Test your sidebar with keyboard only: Tab into the sidebar, use Enter or Space to toggle a category, then Tab to a child link, activate it, and Tab back. Focus should land predictably. If you observe the trap, swizzle the DocSidebarItem component (run npm run swizzle @docusaurus/theme-classic DocSidebarItemCategory --eject) and audit the focus management. The fix is usually to remove the manual setFocus calls and let the browser's default tab order handle navigation.
When Docusaurus integrates Algolia DocSearch (via the @docusaurus/preset-classic), the search button opens a modal dialog that should have role='dialog', aria-modal='true', and a programmatic accessible name pointing at the modal heading. Older DocSearch versions (pre-3.6) used role='combobox' but no aria-label, and screen-reader users heard only 'dialog' with no description. Initial focus also did not always land in the search input.
Upgrade @docsearch/react to version 3.6 or later (npm install @docsearch/react@latest) which fixes the dialog labeling and initial focus issues. If you cannot upgrade, swizzle the SearchBar component and add aria-label='Search documentation' to the trigger button and verify the modal sets focus on the input element on open. The DocSearch GitHub issue #2042 tracks this regression and contains workaround code if you are on an older Docusaurus 2 site.
Docusaurus auto-generates the page H1 from the front-matter title field. Writers familiar with markdown sometimes redundantly add a single # at the top of the body, producing two H1s on the rendered page. Conversely, writers who omit the front-matter title and start the body with ## end up with no H1 at all. Both fail WCAG 1.3.1 because the heading hierarchy is broken.
Establish a rule in your docs contribution guide: never use # headings in MDX body content. The front-matter title field is the only H1. Use ## for major sections, ### for subsections, #### for sub-subsections. Add a markdown lint rule with markdownlint-cli2 (specifically MD025 'multiple top-level headings' and MD002 'first heading should be top level') to enforce in CI. Configure remark-validate-headings as a Docusaurus remark plugin to catch this at build time.
---
title: Authentication
---
# Authentication
This page covers the authentication API.
#### OAuth 2.0 flow
#### API keys ---
title: Authentication
---
This page covers the authentication API.
## OAuth 2.0 flow
## API keys Docusaurus's Tabs and TabItem components implement an ARIA tablist pattern, but in older versions (pre-3.4) the keyboard arrow-key handler did not always update tabindex correctly when authors used the queryString prop or default values. Users navigating with arrow keys could land on a tab visually two positions away from the focused indicator. WCAG 2.4.3 requires focus order to follow visual order.
Upgrade to Docusaurus 3.4 or later which fixes the tab order issue. Test every Tabs component on your docs site with keyboard only: focus the first tab, press right arrow, and confirm focus and visual indicator both move to the next tab. If you cannot upgrade, swizzle the Tabs component (npm run swizzle @docusaurus/theme-classic Tabs --eject) and replace the focus management logic with the latest upstream code from the Docusaurus repository main branch.
The version dropdown in the navbar (used by docs sites that maintain multiple versions like API v1, v2) renders as a div role='button' with no native focus styling. When focused via keyboard, no outline appears, leaving keyboard users unable to see which control has focus.
Add focus-visible styles to your custom CSS at src/css/custom.css. The selector .navbar__link[role='button']:focus-visible should add an outline of at least 2px solid with 3:1 contrast against the navbar background. Test in light and dark mode separately. The rule should also apply to the language dropdown if your docs are internationalized.
/* No focus styles defined */
.navbar__link {
color: var(--ifm-navbar-link-color);
} .navbar__link:focus-visible,
.navbar__link[role='button']:focus-visible {
outline: 2px solid var(--ifm-color-primary);
outline-offset: 2px;
} Docs that use the @docusaurus/theme-live-codeblock plugin render an editable code area where readers can modify and re-execute code samples. The default theme renders this as a contenteditable div without label or aria-labelledby pointing at any heading. Screen-reader users encounter a focusable region with no description.
When you swizzle the Playground component (npm run swizzle @docusaurus/theme-live-codeblock Playground --eject), wrap the editor in a labelled container: <div role='region' aria-labelledby='playground-heading-{id}'> and place a visually-hidden h3 with id matching the aria-labelledby. Provide an explanatory paragraph above the editor that describes what the example demonstrates so screen-reader users can decide whether to engage with the live editor.
Docusaurus admonition syntax (:::note, :::tip, :::warning, :::danger) renders boxes distinguished primarily by background color and an icon in the corner. The default theme does include the type as a heading inside each box (Note, Tip, Warning, Danger), but custom themes sometimes hide the heading via CSS to save vertical space, leaving only color and icon to convey the severity.
In your custom CSS, never hide the admonition heading. If your design requires a more compact look, reduce the font size and weight rather than display: none. Confirm by viewing the rendered admonition with a screen reader: it should announce 'Warning' or 'Danger' before reading the body text. The role='note' on info-style admonitions and the explicit text label provide redundancy with color and icon.
Docusaurus-Specific Tips
- Docusaurus uses React Router under the hood, which means client-side route changes do not trigger a screen-reader page-change announcement by default. Add a custom client module that focuses the H1 on route change or announces the new page title via an aria-live region. The @docusaurus/plugin-debug source has examples.
- When swizzling theme components, prefer the wrapper option over eject when possible. Wrappers receive future upstream accessibility fixes automatically, while ejected components are frozen at the version you swizzled.
- Internationalized Docusaurus sites must set the lang attribute on the html element correctly per locale. Verify by switching locales and inspecting the rendered HTML. Misset lang attributes are a frequent WCAG 3.1.1 violation in i18n docs.
- Run pa11y-ci or axe-core in CI on every pull request that touches MDX content. Algolia and Redux's Docusaurus repos both publish their CI configs publicly as a starting reference.
- If you build a custom plugin that injects HTML into pages (banners, version-deprecation notices, license footers), audit the injection point for landmark conflicts. Inserting a second <main> element or nesting <nav> inside the article is a common bug.
Recommended Tools
axe-core React
React integration for the axe accessibility engine that runs WCAG checks on every render in development mode, surfacing violations directly in the browser console. Wire into your Docusaurus dev build with src/clientModules/axe-init.ts.
pa11y-ci
Command-line accessibility tester that runs WCAG checks against a list of URLs. Pair with the Docusaurus sitemap.xml output to test every published doc page on every deploy.
remark-validate-headings
Remark plugin that integrates into the Docusaurus MDX pipeline to catch heading hierarchy bugs at build time before they ship to production.
Further Reading
Other CMS Checklists
Get our free accessibility toolkit
We're building a simple accessibility checker for non-developers. Join the waitlist for early access and a free EAA compliance checklist.
No spam. Unsubscribe anytime.