EduFlow

Section 2 · Semantic HTML First

Landmarks and Headings

18 min3 min readCode playground

Lesson at a glance

Turn a page of anonymous divs into landmarks and a logical heading outline that screen reader users can scan and jump through in seconds.

  1. 1Landmarks: the regions of a page
  2. 2Naming regions, and how many to use
  3. 3Headings: the outline people navigate

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

Samir once described a badly built page to me as “a phone book with the names torn off”. Everything is there, but nothing tells him where one part ends and the next begins. Ticketly’s home page is exactly that: every block is a div, and the biggest text is a div with a large font. A sighted user sees a header, a menu, a festival title, a line-up and a footer. A screen reader sees one long stream of text.

Landmarks: the regions of a page

HTML has elements that describe the role of a whole region. The browser maps each to an ARIA landmark, and screen readers let users list landmarks or jump between them (the D key in NVDA, the rotor in VoiceOver).

Element Landmark role Notes
<header> banner Only when it is a direct part of the page, not inside article, aside, main, nav or section
<nav> navigation Name it when there are several
<main> main Exactly one visible per page
<aside> complementary Related content such as a sidebar
<footer> contentinfo Same scoping rule as header
<form>, <section> form, region Only when they have an accessible name
<search> search Wraps a site search form

Here is Ticketly’s skeleton before and after:

before.html
<div class="top">
  <div class="logo">Ticketly</div>
  <div class="links"><a href="/events">Events</a> <a href="/faq">FAQ</a></div>
</div>
<div class="content">
  <div class="title">Harbour Lights 2026</div>
  …
</div>
<div class="bottom">© 2026 Ticketly · <a href="/terms">Terms</a></div>
after.html
<header>
  <a href="/" class="logo">Ticketly</a>
  <nav aria-label="Site">
    <a href="/events">Events</a> <a href="/faq">FAQ</a>
  </nav>
</header>
<main>
  <h1>Harbour Lights 2026</h1>
  …
</main>
<footer>© 2026 Ticketly · <a href="/terms">Terms</a></footer>

The visual design doesn’t change at all; these elements have no special styling beyond display: block. What changes is the accessibility tree: Samir can now press D to land on “main landmark” and start reading the festival content, skipping the header entirely.

The page is divided into a banner containing a site navigation, a main region holding the h1 Harbour Lights 2026 with two h2 sections, Line-up and Tickets, where Tickets contains two h3 items, and a contentinfo footer. header (banner) nav "Site" main footer (contentinfo) h1 Harbour Lights 2026 h2 Line-up h2 Tickets h3 Friday pass h3 Weekend pass
Ticketly’s landmarks and heading outline

Naming regions, and how many to use

A <section> on its own is just a generic container. Give it a name and it becomes a region landmark that appears in the landmarks list. The neatest way is to point at its own heading, so the name and the visible text can never drift apart:

HTML
<section aria-labelledby="tickets-heading">
  <h2 id="tickets-heading">Tickets</h2>
  …
</section>

Don’t turn every section into a region, though. Landmarks work like the chapter list of a book: five to eight entries are useful, thirty are not. The rule of thumb is that every piece of content lives inside some landmark (automated checkers flag text that doesn’t), and that only the big, distinct areas a user might want to jump to get one of their own.

Headings: the outline people navigate

Landmarks are the rooms of the page; headings are the signs on the doors. Most screen reader users find content by jumping from heading to heading (H in NVDA and JAWS, or 1 to 6 for a specific level), or by opening a list of all headings and typing to filter it. That list is only useful if the headings form a real outline.

Three rules cover almost every case:

  1. One h1 that says what the page is. On Ticketly, “Harbour Lights 2026”, not the logo.
  2. Levels describe nesting. A section’s subsections are one level deeper. Going back up is fine (h3 to h2); skipping down (h2 to h4) suggests a missing level.
  3. Every heading has content under it, and every chunk of content worth finding has a heading.

Ticketly’s tickets area uses h4 and h5 because a designer wanted smaller text. Fix the structure first, then style:

HTML
<h2>Tickets</h2>
<h3 class="pass-title">Friday pass</h3>
<h3 class="pass-title">Weekend pass</h3>
CSS
.pass-title {
  font-size: 1rem;
  font-weight: 600;
}

Don’t forget the page’s <title> either. It is the first thing a screen reader announces when the page loads and what appears in tabs and history (2.4.2 Page Titled). Put the specific part first: <title>Harbour Lights 2026 tickets · Ticketly</title>.

With the skeleton in place, the next lesson tackles the parts users actually click: buttons, links, and the clickable divs pretending to be both.

Key takeaways

  • The elements header, nav, main, aside and footer create landmarks that screen reader users can list and jump between.
  • A page has one main landmark; when there are several nav or aside regions, give each a distinct accessible name.
  • Headings form the page’s outline; choose the level for structure, never for font size, and style the size with CSS.
  • Start with one h1 that names the page, and don’t skip levels on the way down.

Further reading

Check your understanding

4 questions

Question 1 of 4

Ticketly has two navigation menus: the site menu at the top and a “Festival days” menu in the sidebar. A screen reader lists the landmarks as “navigation, navigation”. What is the best fix?

Question 2 of 4

A designer wants the “Friday pass” title to look small, so the developer used an h5 right after an h2. What is wrong, and what is the fix?

Question 3 of 4

Which element creates a landmark only when it has an accessible name?

Question 4 of 4

On a long Ticketly page, what does a screen reader user most often do first to find the “Refund policy” part?

Practice

Code playground

Give Ticketly a skeleton

Ticketly’s home page is built from anonymous divs, and its headings were chosen for their font size. Restructure the markup without changing the text:

  • Wrap the logo and the menu in a <header>, and make the menu a <nav> (give it a name such as aria-label="Site").
  • Put the festival content in a single <main>.
  • Make the copyright line a <footer> outside main.
  • Turn “Harbour Lights 2026” into the only <h1>, and fix the other headings so the outline has no skipped levels: Line-up and Tickets are sections; the two passes belong under Tickets.
<div class="top">
  <div class="logo">Ticketly</div>
  <div class="links">
    <a href="#events">Events</a>
    <a href="#faq">FAQ</a>
    <a href="#account">Account</a>
  </div>
</div>
<div class="content">
  <div class="title">Harbour Lights 2026</div>
  <p>Two nights of music on the old harbour wall, 12 and 13 June.</p>
  <h4>Line-up</h4>
  <ul>
    <li>Nour Quartet</li>
    <li>The Saltmarsh Band</li>
  </ul>
  <h4>Tickets</h4>
  <h5>Friday pass</h5>
  <p>SAR 180, standing.</p>
  <h5>Weekend pass</h5>
  <p>SAR 320, both nights.</p>
</div>
<div class="bottom">© 2026 Ticketly · <a href="#terms">Terms</a></div>Loading the editor…

Ctrl/⌘ + Enter to run · Runs in a sandbox in your browser. Nothing leaves your device.

Preview

Console

(no output)

Checks

  • There is exactly one main landmark holding the festival content
  • A top-level header contains a nav with the menu links
  • A top-level footer contains the Terms link
  • “Harbour Lights 2026” is the page’s only h1
  • Headings start at h1 and never skip a level; the passes sit one level under Tickets