No main-content region as a bypass target
landmarks/main-missing
- WCAG success criterion
- 2.4.1 (level A)
- Outcome class
- Hint only. An automated check that by design reports review hints only: its findings are marked for manual review, never as confirmed violations.
- Area
- Accessibility, Navigation & Operation
- Default severity
- medium
- axe-core rule
landmark-one-main
Why we flag this
Keyboard users have no reliable target to bypass repeated navigation blocks.
No element with role="main" (implicit via <main> or explicit) present.
What the rule checks
Scope. Every top-level document. A body without any content has nothing to structure and is not applicable.
Expectation. Flags a document without a rendered main landmark (main element or role="main", also inside an open shadow root), at the html element. While a modal dialog is open the absence concerns the moment of measurement: review instead. A hidden second main does not stand in for nor block the visible one. Best practice without a criterion; a missing bypass mechanism is headings/none.
Source. Best practice: WAI-ARIA 1.2 main role; ARIA11; axe landmark-one-main (bypass itself is headings/none, SC 2.4.1)
Detection: shared; needs dom, computed_style.
Failing example
The check flags this for review. Corpus case missing_main_landmark. The page may contain further findings for other rules.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Missing main landmark corpus case</title>
</head>
<body>
<div>
<h1>Page without a main landmark</h1>
<p>No main element or role="main" anywhere on this page.</p>
</div>
</body>
</html>Passing example
The check passes here and reports nothing. Corpus case landmark_main_in_shadow_root. The page may contain further findings for other rules.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Main landmark inside a shadow root corpus case</title>
</head>
<body>
<!-- The page's landmarks live in an open shadow root, as in web-component
app shells. Chrome's accessibility tree exposes the main landmark; a
document.querySelectorAll('main') check does not see it and used to
report a missing main. -->
<app-shell></app-shell>
<script>
const root = document.querySelector('app-shell').attachShadow({ mode: 'open' });
root.innerHTML =
'<header><nav aria-label="Site"><a href="/">Home</a></nav></header>' +
'<main><h1>Content in a shadow root</h1><p>Text.</p></main>' +
'<footer>Footer</footer>';
</script>
</body>
</html>This page is generated from the rule catalogue and the rule contracts; see therule overview for how the outcome classes are derived.