A page built entirely from div elements looks the same in a browser as one built from main, nav and footer. The difference is invisible until something other than a pair of eyes has to read it. A screen reader user pressing the key that jumps to the main content lands nowhere. An agent that fetches the HTML and wants the body, not the menu or the legal footer, has to guess where it starts. These are the same failure, found by the same tools and fixed by the same edit. Check B3 spends two of its four points on landmarks and check C3 spends three of its four on whether things that look like buttons are buttons; between them they are where accessibility work and agent readiness are one job.
This post is the landmark half of B3; the heading half is in one h1 per page. It also covers C3's focusability rules, because a page that has lost its landmarks has usually lost its buttons too.
What a landmark is
HTML has a handful of elements whose job is to say what a region of the page is for rather than how it looks. main marks the content unique to this page; nav marks a block of links for moving around the site; footer, when it sits directly inside the body, marks information about the site as a whole, which is where contact, copyright and policy links usually live. A browser exposes each of these to assistive technology as a landmark role, so a screen reader can list them and jump between them.
ARIA 1.2 defines the same roles as attributes, for cases where the element cannot be changed. role="main", role="navigation" and role="contentinfo" are the equivalents of the three elements above. The ARIA Authoring Practices Guide sets out how to use them, and its first rule settles most arguments: use the native element when one exists, and reach for a role only when you cannot.
For an agent, the value of a landmark is the same as for a screen reader. A fetched page is a tree of a few thousand nodes, and the menu, the cookie notice, the newsletter box and the footer are all text in it. A machine that cannot tell them from the content either reads all of it, at some cost, or picks the largest block of text and hopes. main removes the guess. nav says which links are the site's structure rather than references from within the content. footer says where the site-level links are.
What the scanner measures
B3 is worth four points, two for headings and two for landmarks. The landmark points are awarded together or not at all, and only when every crawled page passes.
For each page, the scanner parses the raw HTML, the first fetch before any JavaScript runs, and asks three questions:
- is there a
mainelement, or an element withrole="main"; - is there a
navelement, or an element withrole="navigation"; - is there a
footerelement, or an element withrole="contentinfo".
If all three are true on every page fetched, the two points are earned. The check is for presence, not position. Two nav elements are fine, and so is a footer inside an article, even though a browser would not expose that one as a landmark; the failure the scanner is looking for is the total absence of structure, not a misplaced element.
The crawl covers up to six pages, the homepage and up to five others chosen from its links, preferring pricing, docs, about, contact and checkout pages, as described on /bot. One page without a main loses both points for the site. Landmarks live in the layout template, so a page missing one is almost always a second template, a legacy page or an error page that returned 200, and the report says which. The evidence block lists pagesCrawled, allLandmarks (how many pages had all three) and problems, where each entry names the page and what it lacked, for example https://example.com/pricing: missing main, footer.
Because the check reads the raw fetch, a shell that serves <div id="root"></div> and renders main and nav after hydration has no landmarks as far as the scanner, or any agent doing a plain fetch, can tell. That is the failure the two fetches describes for text, applied to structure.
Div soup, and why it fails twice
Div soup is the pattern where every element is a div or a span with a class name, and meaning is carried by the CSS and the click handlers. It is the natural output of a component library that ships Box and Flex and leaves the element choice to the developer. Nothing about the page looks wrong.
It fails an accessibility audit under WCAG 2.2 success criterion 1.3.1, Info and Relationships, because the structure a sighted reader sees is not available programmatically, and 2.4.1, Bypass Blocks, because there is no landmark to skip to. It fails B3 for the same reason: a machine cannot build an outline of the page. Anything that reports landmarks for a screen reader user is reporting what an agent gets.
Treat the two as one piece of work. An accessibility remediation that adds the three elements to the layout template has fixed B3's landmark points as a side effect, and agent readiness work that fixes B3 has closed two WCAG criteria. Neither should be budgeted separately.
The interactive half, C3
The component library that produces div soup also produces clickable divs, the most common way a site that looks fine turns out to be undrivable. Check C3, Interactive element semantics, is worth four points and is scored on the homepage:
- two points if there is no
divorspanwith anonclickattribute or withrole="button"; - one point if no element carries a positive
tabindex; - one point for the absence of keyboard traps, awarded without testing, because detecting a trap means driving the page and the scanner does not assert a finding it has no evidence for; the evidence records
keyboardTrapsTested: false.
The evidence also reports realButtons beside clickableNonButtons, so the ratio is visible at a glance.
The focusability rules behind the first two points are short. A button, an a with an href and a form control are focusable by default, in document order; a div is not focusable at all. Adding role="button" changes what a screen reader announces, but it does not make the element focusable, does not make Enter or Space activate it, and does not give it a disabled state. Each has to be added by hand, as the APG's button pattern sets out, and the scanner cannot verify that it was, so it counts the div against you. A native button has all of it built in. WCAG 2.1.1, Keyboard, and 4.1.2, Name, Role, Value, are the criteria on the accessibility side.
Positive tabindex values, tabindex="3" and the like, pull elements to the front of the tab order, so the sequence no longer matches the reading order and every unnumbered element moves behind them. WCAG 2.4.3, Focus Order, is the criterion; the scanner treats any positive value as unsound rather than merely unusual. tabindex="0" and tabindex="-1", which add an element to the natural order or make it focusable by script only, are both fine.
The fix, in the template
For most sites the landmark work is one file: the layout that wraps every page gets a nav around the site navigation, a main around the page-specific content and a footer at the end of the body. If the layout component renders those as div elements, change the element; if you cannot, add the ARIA role to the wrapper it does render.
<body>
<header>
<nav aria-label="Primary">…</nav>
</header>
<main>
<h1>Page title</h1>
…
</main>
<footer>
<nav aria-label="Legal">…</nav>
</footer>
</body>
Then search the component library for click handlers on a div or span and change each to a button, or an a if it navigates; restyling is a few lines of CSS. Remove any tabindex above zero and re-run the scan. Both checks are rated Medium effort in the rubric, which reflects the search through components rather than any single change being hard; the implementation service exists for teams who would rather hand that search over.
Check your own site
Run the free scan and read the B3 problems list and the C3 evidence; they name the pages and count the elements. The full scoring for both is on the methodology page under understanding and actionability.