# Missing <main> landmark

> Without a <main> landmark, assistants and screen readers can’t find your primary content. Wrap the main content in a single semantic <main>.

_Category: AI readiness · Detector `aeo` · Check `no-main-landmark` · Severity: info_

Landmarks like `<main>`, `<nav>`, and `<article>` tell assistive tech and content extractors where the primary content is, instead of forcing them to guess from a sea of `<div>`s. Screen-reader users jump straight to `<main>`; answer engines use it to separate content from chrome. AI scaffolds output `<div>`-only trees, so this signal is simply absent.

## Symptoms

- Screen readers offer no "skip to main content" landmark
- The page is a tree of `<div>`s with no semantic regions
- Content extractors include nav/footer boilerplate as if it were content
- Accessibility audits flag "no main landmark"

## How VibeCheck detects it

The `aeo` detector flags this live in the browser and reports it to the widget's Problems list — and to your coding agent over MCP.

- **Issue string:** `No <main> landmark`
- **Threshold:** document.querySelector("main") returns null

## Root causes

- The layout uses `<div>` everywhere instead of semantic elements
- A component library wraps content without a `<main>` region
- Multiple content areas but no single primary landmark

## The fix

Wrap the primary content of each page in exactly one `<main>` element, and use `<nav>`, `<header>`, `<footer>`, and `<article>` for the surrounding regions. There should be one `<main>` per page, containing the content unique to that page (not the shared chrome).

### Steps

1. Identify the primary content region unique to the page
2. Wrap it in a single `<main>` element
3. Use `<nav>`/`<header>`/`<footer>` for the surrounding chrome

```html
<body>
  <header><nav>…</nav></header>
  <main>
    <h1>Page title</h1>
    <!-- the content unique to this page -->
  </main>
  <footer>…</footer>
</body>
```

## Framework-specific fixes

### React

Render a `<main>` in your layout around the routed content — one per page, wrapping only the page-specific content, not the shared nav/footer.

```tsx
<>
  <SiteHeader />
  <main>{children}</main>
  <SiteFooter />
</>
```

### Vue

Use `<main>` around `<router-view>` (or the page content) in your app shell template.

```vue
<template>
  <SiteHeader />
  <main><router-view /></main>
  <SiteFooter />
</template>
```

### Svelte

Wrap the page slot in `<main>` in your root `+layout.svelte`.

```svelte
<SiteHeader />
<main>
  <slot />
</main>
<SiteFooter />
```

### Vanilla JS

Use the `<main>` element in your HTML structure and reserve `<div>` for styling-only wrappers.

```html
<main id="content">
  <h1>…</h1>
</main>
```

## FAQ

### Can I have more than one <main>?

Only one `<main>` should be visible per page. You may have multiple in the DOM if all but one are hidden, but the simplest and safest approach is exactly one visible `<main>` per page.

### What goes inside <main> vs outside?

`<main>` holds the content unique to this page. The site header, primary navigation, and footer are repeated across pages, so they belong in `<header>`, `<nav>`, and `<footer>` outside `<main>`.

## Related problems

- [No H1 heading](https://vibecheck.wcgw.fun/fix/missing-h1-heading.md) — Search visibility
- [Missing structured data (JSON-LD)](https://vibecheck.wcgw.fun/fix/missing-structured-data.md) — AI readiness
- [Missing author and date signals](https://vibecheck.wcgw.fun/fix/missing-author-metadata.md) — AI readiness
- [Images missing alt text](https://vibecheck.wcgw.fun/fix/missing-image-alt-text.md) — Search visibility

---

Fix guide from VibeCheck — https://vibecheck.wcgw.fun/fix/missing-main-landmark. Full site index for LLMs: https://vibecheck.wcgw.fun/llms.txt
