Modern HTML5 in Production · Semantics and Accessibility
Landmarks and Headings: Pages You Can Navigate
Why screen-reader users abandoned a checkout built from divs, and how semantic elements and a real heading outline let people jump straight to what they need.
Scenario
Example app: an online shop's product page is built entirely from <div>s with classes like header, menu, content and title. It looks fine. An accessibility audit before a public-sector contract includes testing with screen-reader users.
What you see in production
- Screen-reader users have to listen to the whole navigation menu (40 links) on every page before reaching the product.
- The screen reader's landmarks and headings lists, the main way these users skim a page, are empty.
- The audit fails WCAG success criteria for info and relationships and for bypassing blocks.
The naive code
<div class="header"><div class="logo">Shop</div><div class="menu">...40 links...</div></div>
<div class="content">
<div class="title">Ceramic Mug</div>
<div class="subtitle">Reviews</div>
</div>
<div class="footer">...</div>
Reproduce it
Screen readers build their navigation lists from landmark elements (header, nav, main, aside, footer, form with a name, section with a name) and headings (h1 to h6). The simulation lists what a screen reader offers for each version of the page.
const LANDMARKS = { header: "banner", nav: "navigation", main: "main", aside: "complementary", footer: "contentinfo" };
function outline(page) {
const landmarks = page.filter((el) => LANDMARKS[el.tag]).map((el) => LANDMARKS[el.tag]);
const headings = page.filter((el) => /^h[1-6]$/.test(el.tag)).map((el) => `${el.tag}: ${el.text}`);
return { landmarks, headings };
}
const divSoup = [
{ tag: "div", text: "Shop" }, { tag: "div", text: "menu (40 links)" },
{ tag: "div", text: "Ceramic Mug" }, { tag: "div", text: "Reviews" }, { tag: "div", text: "footer" },
];
const semantic = [
{ tag: "header", text: "Shop" }, { tag: "nav", text: "menu (40 links)" }, { tag: "main", text: "" },
{ tag: "h1", text: "Ceramic Mug" }, { tag: "h2", text: "Reviews" }, { tag: "h2", text: "Delivery" },
{ tag: "footer", text: "footer" },
];
for (const [label, page] of [["div soup", divSoup], ["semantic HTML", semantic]]) {
const { landmarks, headings } = outline(page);
console.log(`${label}:`);
console.log(` landmarks: ${landmarks.length ? landmarks.join(", ") : "(none)"}`);
console.log(` headings: ${headings.length ? headings.join(" | ") : "(none)"}`);
}
Output
div soup: landmarks: (none) headings: (none) semantic HTML: landmarks: banner, navigation, main, contentinfo headings: h1: Ceramic Mug | h2: Reviews | h2: Delivery
Root cause
<div> has no meaning: browsers expose it to assistive technology as a generic container. Visual styling (large bold text, a coloured bar) tells sighted users what's a heading or a menu, but none of that reaches the accessibility tree.
The fix
- Use landmark elements: one
<header>,<nav>(witharia-labelif there are several), one<main>,<aside>,<footer>. See the landmark regions guide. - Use one
<h1>for the page's main topic and nest<h2>to<h6>in order, without skipping levels for styling. Style headings with CSS, not by choosing a smaller tag. - Add a skip link (
<a href="#main" class="skip-link">Skip to content</a>) as the first focusable element. - Use
<article>,<section>(with a heading),<ul>/<ol>and<table>for what they mean.
Trade-offs
- Semantic elements cost nothing in performance and usually reduce the CSS and ARIA you need.
- Design systems sometimes force heading levels for looks; separate visual style (classes) from heading level.
Detect it in production
- Automated checks (axe, Lighthouse accessibility audit) flag missing landmarks, empty headings and skipped levels.
- Manually list landmarks and headings with a screen reader (NVDA's elements list, VoiceOver's rotor) on key templates.
Key points
- Screen-reader users navigate by landmarks and headings;
<div>s provide neither. - Use
header,nav,main,aside,footer, a singleh1and an ordered heading outline. - Add a skip link and check pages with automated tools and a real screen reader. See WCAG 2.2.
Practice questions
Why do semantic elements such as nav and main matter for accessibility?
Answer
They create landmarks in the accessibility tree, so screen-reader users can list them and jump straight to the navigation or main content. Divs are generic containers and expose nothing.
What makes a good heading structure?
Answer
One h1 describing the page, then h2 to h6 nested in order without skipping levels, each describing the section that follows. Choose levels by structure and style them with CSS.
What is a skip link?
Answer
A link, usually the first focusable element, that jumps to the main content so keyboard and screen-reader users don't have to pass through the navigation on every page.
Open in the app to mark lessons complete, take notes and track your progress.