Accessibility checkers have a problem with the placement of the h1 tag
Have you read the Contributing Guidelines on issues?
- [x] I have read the Contributing Guidelines on issues.
Prerequisites
- [x] I'm using the latest version of Docusaurus.
- [x] I have tried the
npm run clearoryarn clearcommand. - [x] I have tried
rm -rf node_modules yarn.lock package-lock.jsonand re-installing packages. - [x] I have tried creating a repro with https://new.docusaurus.io.
- [x] I have read the console error message carefully (if applicable).
Description
Accessibility checkers (such as SiteImprove) are flagging each page using the blog layout as not adhering to "accessibility best practices" with the following note:
Page does not start with a level 1 heading A level 1 heading h1 tells the user what the page is about before they decide to navigate through the content.
Headings are used to structure content into a hierarchy of importance. If a page starts with a different heading level, it can cause confusion for the user.
When analyzing the HTML output from Docusaurus, the sidebar (aside) is rendered before the main content (main) and the h3 tag in the sidebar is being detected as the first header in the HTML source. By removing the use of h3 in the sidebar and replacing with a div class specific to the sidebar, accessibility checkers (and screen readers and other assistive devices) will see the use of h-tags in the main content as the only actual headings on the page.
Source example below:
<div class="row">
<aside class="col col--3">
<nav class="sidebar_re4s thin-scrollbar" aria-label="Blog recent posts navigation">
<div class="sidebarItemTitle_pO2u margin-bottom--md">Recent posts</div>
<div role="group">
<h3 class="yearGroupHeading_rMGB">2025</h3>
<ul class="sidebarItemList_Yudw clean-list">
[...]
</ul>
</div>
</nav>
</aside>
<main class="col col--7">
<article class="margin-bottom--xl">
<header>
<h2 class="title_f1Hy"><a href="/blog/item1/">Article Title</a></h2>
[...]
</header>
<div class="markdown">
<p>Article body.</p>
</div>
[...]
</article>
</main>
</div>
Related to this -- the blog index page does not contain an h1 tag altogether.
Reproducible demo
No response
Steps to reproduce
- Publish a Docusaurus site using
docusaurus-theme-classicand create a blog page. - Review the HTML code.
Expected behavior
The first header tag on a blog page should be an h1 that conveys the article title.
An h1 tag on the blog index (e.g., recent posts) page should be an h1 that conveys the page title.
Actual behavior
The first header tag to occur on the blog pages is an h3 in the sidebar.
No h1 tag exists on the blog index page.
Your environment
- Public source code:
- Public site URL:
- Docusaurus version used:
- Environment name and version (e.g. Chrome 89, Node.js 16.4):
- Operating system and version (e.g. Ubuntu 20.04.2 LTS):
Self-service
- [ ] I'd be willing to fix this bug myself.
Hey, I'm not an accessibility expert but this indeed seems weird to use an <h3> for the "year groups" of our blog sidebar.
However,
Accessibility checkers (such as SiteImprove)
Can you tell me which other accessibility checker report this error? How can I verify this myself?
I've come to not trust blindly what a single accessibility tool report, because they may all behave differently in practice.
Also, do you have any reference document from an authoritative source that explains that it's bad to use an h3 in this <aside> context? I'm not sure but I think it's fine to use headings outside the main content.
See for example: https://www.w3.org/WAI/tutorials/page-structure/headings/#:~:text=used%20like%20this.-,Main%20heading%20after%20navigation,-In%20this%20second
TLDR: can you help me and somehow find a way to prove that it's not your specific tool that is wrong and reporting a false positive.
When analyzing the HTML output from Docusaurus, the sidebar (aside) is rendered before the main content (main) and the h3 tag in the sidebar is being detected as the first header in the HTML source. By removing the use of h3 in the sidebar and replacing with a div class specific to the sidebar, accessibility checkers (and screen readers and other assistive devices) will see the use of h-tags in the main content as the only actual headings on the page.
Unrelated to the issue being reported, I also feel that the sidebar with years and other blog posts could also semantically be a nav item since it allows you to navigate to other posts while also changing the URL
We are seeing similar issues on https://developer.overheid.nl/communities/
index.mdx (source):
# Communities
Hieronder een overzicht van developer communities.
```mdx-code-block
import DocCardList from '@theme/DocCardList';
<DocCardList />
```
Rendered output (cut some SVG elements out for readability):
<header>
<h1>Communities</h1>
</header>
<p>Hieronder een overzicht van developer communities.</p>
<section class="row">
<article class="docCardListItem_d7Ib col col--6">
<a class="card padding--lg cardContainer_S8oU" href="/communities/code-for-nl">
<h3 class="cardTitle_HoSo"><!-- -->Code for NL</h3>
<p class="cardDescription_c27F">Code for NL is een netwerk van developers, designers en andere experts die
samenwerken aan digitale toepassingen voor een open, eerlijke en inclusieve samenleving.</p>
</a>
</article>
<article class="docCardListItem_d7Ib col col--6">
<a class="card padding--lg cardContainer_S8oU" href="/communities/common-ground">
<h3 class="cardTitle_HoSo"> <!-- -->CommonGround</h3>
<p class="cardDescription_c27F">Gemeenten werken samen met een community van betrokkenen partijen aan
bouwstenen, zoals softwaretoepassingen, technische componenten of standaarden voor gegevensuitwisseling.
</p>
</a>
</article>
</section>
Note that, despite DocCard setting the heading to h2 (https://github.com/facebook/docusaurus/blob/a4c33bfea56b231b302adbf819a45c2de217a01d/packages/docusaurus-theme-classic/src/theme/DocCard/index.tsx#L79), the heading is rendered as h3.
When we run npx axe https://developer.overheid.nl/communities/ we get the following output:
Running axe-core 4.10.3 in chrome-headless
Testing https://developer.overheid.nl/communities/ ... please wait, this may take a minute.
Violation of "heading-order" with 1 occurrences!
Ensure the order of headings is semantically correct. Correct invalid elements at:
- a[href$="code-for-nl"] > h3
For details, see: https://dequeuniversity.com/rules/axe/4.10/heading-order
1 Accessibility issues detected.
Which is a correct finding, since the heading order is h1 for "Communitities" and then h3 for "Code for NL". It should be h2 for "Code for NL", which is what DocCard wants it to be, yet it isn't rendered as such.
This is for Docusaurus 3.8.1 (latest version at time of writing).
@TimvdLippe are you sure you didn't swizzle the components?
Because we don't see this behavior on our own website: https://docusaurus.io/docs/sidebar/items#embedding-generated-index-in-doc-page
Ah, I am not familiar that much with Docusaurus and didn't know swizzling. Turns out that you are correct. Haven't been able to figure out how to fix the issue, given that the h1 header is generated by Docusaurus. I tried swizzling https://github.com/facebook/docusaurus/tree/main/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage (which is marked as unsafe, I know) to make it a h2, but that didn't have any effect. Will have to debug further what's going on with our setup.