Your Root Layout Owns the main. Don't Write Another One.
The Next.js root layout for this site ends like this:
<PageTransition>
<main className="pt-16">{children}</main>
</PageTransition>
Every page rendered by the app gets wrapped in that <main>. The blog page, the services page, the contact page — all of them live inside a single <main> element provided by the layout.
When we built the audit page, the first draft looked like this:
return (
<main className="min-h-screen bg-white text-neutral-900">
<div className="mx-auto max-w-3xl px-6 py-12 space-y-10">
{/* content */}
</div>
</main>
);
That produced two <main> elements: the one in the root layout, and the one in the page component. Nested, stacked, two landmarks in the document at the same time.
Why It Matters
The <main> element is an ARIA landmark. Screen readers expose it as a navigation target. A user on VoiceOver or NVDA can jump directly to the main content by pressing a key — they don't have to tab through the header, nav, and breadcrumb to get there. That's the whole point of the landmark.
HTML has a rule: only one <main> should be visible at a time. The spec allows multiple <main> elements in a document, but only if the others are hidden with display: none or hidden. Two visible, nested <main> elements is undefined behavior for assistive technology. Some screen readers surface both as navigation targets. Some ignore the inner one. Some announce both but treat neither as authoritative. None of those outcomes are correct.
This isn't a theoretical problem. We test with axe-core as part of the build process, and "multiple main landmarks" is a moderate violation that appears in reports. It's also the kind of thing that affects real users who depend on keyboard navigation.
The Fix
The audit page is a self-contained document: a report prepared for a specific business, with its own header, sections, footer, and CTA. That's exactly what <article> is for.
// <article>, not <main> — the root layout already renders the page-level <main>
return (
<article className="min-h-screen bg-white text-neutral-900">
<div className="mx-auto max-w-3xl px-6 py-12 space-y-10">
{/* content */}
</div>
</article>
);
<article> represents independently distributable or reusable content — news articles, forum posts, reports. An ops audit fits that definition cleanly. It has its own <header> with the title and date, its own sectioned content, its own footer disclaimer. It would make sense in isolation if you pulled it out of the layout.
The page structure now has exactly one <main>: the one in the layout. The audit content lives in <article> inside that <main>. The landmark tree is unambiguous.
The Pattern to Watch For
The same bug is easy to introduce on any page that needs a full-bleed background or its own visual container. The instinct is to write <main className="bg-white"> or <main className="min-h-screen"> for the styling, without realizing the layout is already providing the structural landmark.
The rule: the root layout owns the <main>. Page components provide content, not landmarks. If a page needs a wrapper for styling, use <article> for self-contained documents, <section> for thematic grouping, or a plain <div> if the content doesn't have standalone meaning. None of those create a duplicate landmark.
Check your Next.js pages against this. If any of them open with <main>, you're probably shipping two.