CSE 134B Web Client Languages
  1. Structure
  2. HTML
  3. Landmarks and sectioning

Lesson 05 · Semantics

Landmarks and sectioning

A valid page can still say almost nothing about itself. Build it from divs and it renders perfectly, passes the validator, and tells every reader that is not a sighted mouse user that it is one long, undivided stream. This lesson starts the Semantics group with the largest parts of a page: the elements that say "this is the site header," "this is the main content," "this is navigation," and the landmarks that assistive technology builds from them.

Learning objectives

  • Tell presentational markup from semantic markup, and explain why HTML has carried both
  • Recognize div-itis and class-itis in real markup, and cure them
  • Describe the accessibility tree, and how elements become roles and landmarks in it
  • Choose among header, footer, main, nav, aside, section, article and search, and predict which landmark each one produces, if any
  • Refactor a div-soup page into landmarks, and check the result in DevTools

Presentational and semantic markup

Lesson 01 split markup languages into two camps. Presentational markup says how something looks. Semantic markup says what something is. HTML has struggled with the split for most of its life, because it carried both kinds at once: font, big, small and b on one side, em, p, blockquote and h1 on the other. Each new version of the standard has tried to move appearance out to CSS and leave HTML with meaning.

The smallest example is a pair that looks identical in every browser. <b> was born as "bold." <strong> was born as "this matters." Both render heavy by default, so on screen they are indistinguishable. Now restyle them. Make strong red, and every important phrase on the site turns red, which is a sane result: the style follows the meaning. Make b red, and you have an element whose name says "bold" and whose look says "red," which is, as the slides put it, kind of crazy. HTML5 redefined b with a meaning of its own, and lesson 06 sorts out the text-level elements in detail. The point here is the habit of mind: pick the element for what the content is, and let CSS decide how it looks.

Even semantic elements carry a presentational bias in authors' heads. Ask a beginner what h1 does and the answer is usually "it makes text big." It does not. It says "this is the top-level heading," and the browser's default style sheet happens to make it big. A heading styled small is still a heading; a div styled big is still nothing.

Catching div-itis

Semantic markup sounds good, and the web mostly ignores it. The HTTP Archive's Web Almanac for 2024 counted the elements on millions of pages and found div "by far the most dominant element": 28.7% of all elements on mobile pages, more than links (12.6%) and span (11.2%) put together. Nearly one element in three on the web is a box that means nothing. The course calls this div-itis.

Think of div as the "um," "you know" or "something" of HTML as a spoken language. A sentence full of them can still get its point across to a patient listener who can see your hands. Take away the hands, and the listener is left with "um, something, you know." A page built from divs works the same way. A sighted user reads the layout and sees the header and the sidebar. A screen reader, a crawler or an AI agent gets only the tree, and in the tree every box is the same kind of box.

It does not matter, until it does: an accessibility audit fails, a crawler picks the wrong text as the content, a new developer reads six nested divs to find the sidebar. That is technical debt, and it compounds.

Here is a realistic case: a small blog page, built the way many templates still build pages. It looks fine.

The standard agrees

If a course's opinion is not enough, the HTML standard says the same thing in a note under the div element:

Authors are strongly encouraged to view the div element as an element of last resort, for when no other element is suitable. Use of more appropriate elements instead of the div element leads to better accessibility for readers and easier maintainability for authors.

Note the second benefit. Accessibility is about your users, and maintainability is about you. Even an author who somehow decided that users' needs were not their problem would still be paying the maintenance bill.

The cure

The cure is mostly vocabulary. Developers reach for div because it is the element they know will work, and the remedy is knowing the alternatives well enough that they come to mind first. When you are making a site header, use header. When you are making the navigation bar, use nav. When you are dividing the content into themed parts with headings, use section. When you are making a button, use button. Each of these is no more typing than the div it replaces, and each gives search engines and screen readers something to work with. The cure is also subtractive, which matters later: it removes markup rather than adding more.

Catching class-itis

class-itis is div-itis's companion. It takes three common forms. The first is long strings of presentational class names, a dozen to an element, each naming a look rather than a meaning:

<figure class="md:flex bg-slate-100 rounded-xl p-8 md:p-0"> <img class="w-24 h-24 md:w-48 md:h-auto md:rounded-none rounded-full" src="…" alt="…"> <div class="pt-6 md:p-8 text-center md:text-left space-y-4"> <blockquote> <p class="text-lg font-medium">…</p>

The second is fake elements hiding in class values: <div class="button">, <div class="header">, <div class="aside">, written by authors who did not know, or did not trust, that the real element existed. The third is the class added "just in case," to a single element that could already be selected by what it is.

The standard has a note about this too, under the class attribute: "There are no additional restrictions on the tokens authors can use in the class attribute, but authors are encouraged to use values that describe the nature of the content, rather than values that describe the desired presentation of the content." A class named text-lg is a font element with extra steps. A class named button on a div is a button with none of a button's behavior: it cannot be focused with the keyboard, Enter and Space do nothing, it does not submit a form, and a screen reader does not announce it as a button. The fake search button on the div-soup page above has exactly these faults.

The cure

  1. Use the built-in element. <button>, not <div class="button">; <nav>, not <div class="nav">.
  2. Select by structure first. Write CSS rules for elements, built-in and custom, and for their place in the tree. Use a class to name a group that differs from the default, and an id for a fragment target or script hook, as lesson 03 explains.
  3. Name what a thing is, not how it looks. If a class is still needed, warning describes the content; red-box describes one design that will change.

This is the course's house style, and the site you are reading follows it: almost no classes, and style sheets that select elements by what they are and where they sit. Some teams choose utility classes on purpose and accept the trade; it should be a choice, not a reflex.

Why semantics matter: the accessibility tree

Semantics stop being a matter of taste once you see where they go. From the DOM, the browser computes a second tree, the accessibility tree, and hands it to the operating system's accessibility interfaces. Screen readers, voice control, switch access and magnifiers all read that tree, not your source and not your pixels.

Each node in the accessibility tree carries a role (what kind of thing it is: link, button, heading, list, navigation), an accessible name (what it is called: the text of a button, the alt of an image, the label of a field), and its states (checked, expanded, disabled). The role comes from the element. <button> becomes a button, <nav> becomes navigation, <h2> becomes a heading at level 2. A div or span becomes the role generic, which assistive technology treats as nothing in particular, and many of them are dropped from the tree entirely. The rules that map each HTML element to a role are written down in a W3C specification, HTML Accessibility API Mappings, or HTML-AAM, and its companion for authors, ARIA in HTML.

A few roles mark out the large regions of a page. These are the landmark roles: banner, navigation, search, main, complementary, contentinfo, region and form. A screen reader can list a page's landmarks and jump straight to any one of them, the way a sighted user glances at the layout and looks straight at the sidebar. When you use semantic markup like header, footer, nav, main, headings, section and article correctly, you give assistive devices and search engines understood landmarks for the structure and meaning of your document. Use divs, and there are none.

The same page as div soup and as landmarksTwo wireframes of the same blog page side by side. On the left, the div-soup version: a header band, a content column, a sidebar and a footer, each labeled div with role generic, all in plain gray boxes, and a red note saying no landmarks, one undivided stream. On the right, the semantic version: the header band is a header element with role banner, holding a nav with role navigation and a search element with role search; the content column is inside main, role main, holding an article; the sidebar is an aside, role complementary, also inside main; the footer is a footer element, role contentinfo.div soupdiv.site-headergenericdiv.postgenericdiv.sidebargenericdiv.site-footergenericno landmarks: one undivided streamlandmarksheaderbannernavsearchmainmainarticlerole article,not a landmarkasidecomplementaryfootercontentinfosix landmarks to jump between
Same layout, same pixels. Only the right-hand version gives assistive technology a map. Shaded boxes are landmarks; the article is a role but not a landmark.

The same structure lets reader modes, search engines and AI agents tell the content from the boilerplate around it. The Accessibility page follows the accessibility tree through the rest of the course.

The elements and the landmarks they make

Eight HTML elements can produce a landmark, and several of them do so only under conditions. The table gives the rule; the subsections give the reasoning.

HTML elements and their landmark roles (HTML-AAM)
ElementLandmark roleCondition
headerbannerOnly when scoped to the body: not inside article, aside, main, nav or section
footercontentinfoThe same condition as header
mainmainAlways; one visible main per page
navnavigationAlways
asidecomplementaryAlways when scoped to the body or main; inside article, aside, nav or section, only with an accessible name
sectionregionOnly with an accessible name
searchsearchAlways
formformOnly with an accessible name

Four of these, article, aside, nav and section, are the standard's sectioning content, the category lesson 04 described as the one that "defines the scope of header and footer elements." The table shows what that scope does in practice.

The standard defines header as "a group of introductory or navigational aids": usually a heading, often a logo, a search form, a table of contents. A footer is "a footer for its nearest ancestor sectioning content element, or for the body element if there is no such ancestor": who wrote it, related links, copyright. Neither is tied to a position on the screen; the standard notes that "footers don't necessarily have to appear at the end of a section, though they usually do."

Both can appear many times on a page, and that is where scope comes in. The page's own header, scoped to the body, becomes the banner landmark, the one region that says "the site around this page." A header inside an article is that article's header, the title and byline of one post, and it would be wrong to announce it as the site's banner. So it does not become one. HTML-AAM now maps it to a newer role, sectionheader, which is not a landmark, and allows browsers to expose nothing at all when it has no name and nothing else worth announcing. footer follows the same rule, with contentinfo for the page and sectionfooter for a section.

Where header and footer become landmarksA tree. body has three children: header, main and footer. The header and footer directly under body are shaded as landmarks and labeled banner and contentinfo. A div wrapper between body and a header would not change this. main has a child article, which has its own header and footer; those are drawn as plain dashed boxes labeled sectionheader and sectionfooter, not landmarks, because article is sectioning content. A dashed boundary marks the article as the scope that stops them.bodyheaderbannermainmainfootercontentinfosectioning content:the scope stops herearticleheadersectionheaderfootersectionfooternot landmarks:a post's title and bylineare not the site's banner
The same element, two meanings. A header or footer counts as a page landmark only when no article, aside, main, nav or section stands between it and the body. A plain div in between does not count.

The rule looks only at those five elements. A header wrapped in a layout div directly inside the body is still the banner; Chrome exposes it as one. A header inside main is not, which is one more reason to keep the site header outside main.

main

main "represents the dominant contents of the document": what this page is about, as opposed to the header, navigation, sidebars and footer that repeat on every page of the site. It made official the <div id="main"> that most sites already had.

The rule is one visible main per page. The standard's exact wording: "a document must not have more than one main element that does not have the hidden attribute specified," which lets a single-page application keep several views in the document and show one at a time. It also must be hierarchically correct: its ancestors may only be html, body, div, a form without an accessible name, and autonomous custom elements. So main never goes inside article, aside, header, footer or nav. The validator enforces both rules. main is also the natural target of a skip link, covered in Usable links.

nav is "a section of a page that links to other pages or to parts within the page," and it always produces the navigation landmark. That is exactly why it should be used sparingly. The standard says so directly: "Not all groups of links on a page need to be in a nav element — the element is primarily intended for sections that consist of major navigation blocks." Its own example is the short row of links in a footer, to the terms of service, the home page and a copyright page, for which "the footer element alone is sufficient."

The site's main menu is a nav; so is a table of contents for a long page, or a set of breadcrumbs. A list of tags under a post, three related links in a sidebar, or the social icons in a footer are not. If everything is a landmark, nothing is. When a page does have more than one nav, name each one, so the landmark list reads "Primary navigation, Breadcrumb navigation" rather than "navigation, navigation":

<nav aria-label="Primary"> … </nav> <nav aria-labelledby="toc-heading"> <h2 id="toc-heading">On this page</h2> … </nav>

Leave the word "navigation" out of the name; the role already says it.

aside

aside holds "content that is tangentially related to the content around the aside element, and which could be considered separate from that content." Sidebars, pull quotes, a box of background facts, an advertisement, a group of secondary navigation blocks. The test is whether the surrounding content still reads correctly with the aside removed. The standard draws the line at parentheticals: "It's not appropriate to use the aside element just for parentheticals, since those are part of the main flow of the document."

A page-level aside, scoped to the body or to main, becomes the complementary landmark. Inside an article or section, HTML-AAM makes it a landmark only if it has a name, so that every pull quote in a long article does not crowd the landmark list. Browsers do not all follow that yet: Chrome 156, tested for this lesson, still exposes an unnamed aside inside an article as complementary. Name the asides you want found, and you get the same result everywhere.

section

section is "a generic section of a document or application," a thematic grouping of content, typically with a heading. The word that misleads people is "generic." The standard follows it at once with a warning:

The section element is not a generic container element. When an element is needed only for styling purposes or as a convenience for scripting, authors are encouraged to use the div element instead.

Its rule of thumb is that a section is appropriate "only if the element's contents would be listed explicitly in the document's outline": if it would have a heading in a table of contents, it is a section. A box that exists to get a background colour is a div.

An unnamed section has the generic role, the same as a div. Give it an accessible name, usually by pointing aria-labelledby at its heading, and it becomes a region landmark. Do that only for a section important enough that a user would want to jump to it from anywhere on the page. The sections of this lesson are unnamed on purpose: headings already let users move between them.

article

article is "a complete, or self-contained, composition in a document, page, application, or site and that is, in principle, independently distributable or reusable, e.g. in syndication." The test is the RSS test: could this be lifted out and shown on its own, in a feed or on another site, and still make sense? A blog post passes; so do a news story, a product card, a forum reply and, in the standard's own list, "a user-submitted comment."

Articles nest. When they do, the standard says "the inner article elements represent articles that are in principle related to the contents of the outer article," which is exactly a post and its comments. article maps to the article role, which screen readers can announce and let users move between, but it is not a landmark. A chapter of a book is a section, since it leans on the chapters around it; a review of the book is an article.

search is the newest element in this lesson. It was added to the standard in 2023 and "represents a part of a document or application that contains a set of form controls or other content related to performing a search or filtering operation": a site search, a filter for a list of results, a global web search. It became Baseline in October 2023, when Chrome, Edge and Firefox 118 and Safari 17 all shipped it, and it has been Baseline widely available since April 2026.

Before it, the only way to mark a search was ARIA, <form role="search">, which still works. The standard adds that search is not for the results themselves, only for the controls that produce them.

<search> <form action="/search"> <label for="q">Search the site</label> <input type="search" id="q" name="q"> <button>Search</button> </form> </search>

form and address

A form becomes a form landmark only when it has an accessible name. HTML-AAM is explicit: "If a form has no accessible name, do not expose the element as a landmark." Most forms should stay unnamed. Name the one or two that a user would want to find directly, such as a checkout or a contact form, and leave the rest alone. The Forms section covers the form itself.

address is often guessed to be a landmark and is not. It "represents the contact information for its nearest article or body element ancestor," which makes it the right element for an author's or a site's contact details, usually in a footer, and the wrong element for postal addresses in general. HTML-AAM maps it to the group role.

div and span: the last resort

None of this makes div and span wrong. They are the generic block and inline containers, they map to the generic role, and they exist precisely for the cases where no element means what you need: a wrapper for a grid or flex layout, a hook for a script, a box that only exists to carry a background. A span around a few words to colour them, when the colour carries no meaning that em, strong or mark would express, is fine. So is the div that holds the landmark inspector in this lesson's demos, chosen because it adds nothing to the landmarks.

The danger is the other cases: the div standing in for a header, a sidebar or a button, with a class name doing the job of an element. Before HTML5 there was no aside, so authors wrote <div class="aside"> or whatever name they invented. Class names carry meaning only for readers who know the convention; elements are the shared vocabulary. A good habit before typing <div is to ask what the box is. If the answer is a noun from this lesson, use that element. If the answer is "a box," use the div.

The outline that never shipped

The sectioning elements once promised more. HTML5 described a document outline algorithm in which each section, article, aside and nav started a new level of the outline, so an h1 nested three sections deep would count as a third-level heading. Authors were told they could use h1 everywhere and let the nesting decide. The slides of the time already called the resulting outline "speculative," with good reason. As lesson 01 tells it, no browser ever exposed the levels it computed to assistive technology (only its default heading sizes shipped, as lesson 06 explains), and in 2022 the standard removed it.

The standard now says plainly: "The outline is all headings in a document, in tree order." Heading levels are what you write. An h1 inside a section is a level 1 heading, whatever wraps it. The sectioning elements still matter, for landmarks, for scoping header and footer, and for meaning, but the outline comes from h1 to h6 alone. Lesson 06 is about writing that outline well.

Moving by landmark

Screen-reader users rarely read a page from top to bottom. They skim, the way sighted users do, using keystrokes that jump to the next heading, link, list, form field or landmark. The keys differ from one screen reader to another, and from version to version, so treat these as examples rather than a reference: in NVDA, D moves to the next landmark and Shift+D to the previous one; in JAWS it is R (older versions used the semicolon); VoiceOver on macOS and iOS gathers landmarks into a list in its rotor. Each also offers a list of all the page's landmarks, named by role and accessible name.

On the semantic version of the demo page, that list reads roughly: banner, Primary navigation, search, main, Comments region, More from the journal complementary, content info. From any point on the page, the post's comments are two keystrokes away. On the div-soup version, the list is empty, and the only way to the comments is to move through the page element by element, or to guess at headings, of which the soup has only three, because its title is a div.

Screen readers differ in what they announce, too, and that is not something to design around. Write the right elements, check the tree in DevTools, and once in a while listen to your own page with the screen reader built into your operating system.

Fixing the problem we made? ARIA roles

There is another way to get landmarks onto a div-soup page: keep the soup and add ARIA. WAI-ARIA, Accessible Rich Internet Applications, defines a role attribute that sets an element's role directly, so <div role="navigation"> produces the same landmark as <nav>.

<!-- A band-aid on the soup --> <div class="nav-links" role="navigation" aria-label="Primary"> … </div> <!-- The element --> <nav aria-label="Primary"> … </nav>

For many teams this is the preferred fix, because it is additive: nothing gets removed, more technology gets layered on top. It treats the symptom. The cure is subtractive: replace the div with the element and the role comes for free. The W3C's guide to using ARIA states this as its first rule:

If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.

For landmarks the difference looks small, since a landmark role has no behavior to imitate. It still matters: the role lives in an attribute that is easy to drop in a refactor, CSS and scripts still have to select .nav-links, and the next developer still reads soup. For anything interactive the difference is large. <div role="button"> is announced as a button and does nothing else a button does: it needs a tabindex, a key handler for Enter and another for Space, and still will not submit a form.

Adding a role that the element already has, as in <nav role="navigation">, is harmless but noisy; ARIA in HTML lists it as allowed but "NOT RECOMMENDED." Roles earn their place where HTML cannot help, such as a legacy template you may not restructure. The aria-label and aria-labelledby attributes in this lesson are ARIA at its best: augmenting a correct element, not replacing one.

From div soup to landmarks

Back to the blog page. The refactor is a series of questions, one per box, and the answer to each comes from this lesson. Start with the soup's skeleton:

<div class="site-header"> <div class="logo"><a href="/">Tide Pool Journal</a></div> <div class="nav-links"> … </div> <div class="search-box"> <input type="search" name="q" placeholder="Search posts"> <div class="btn btn-search">Search</div> </div> </div> <div class="content-wrap"> <div class="post"> <div class="post-header"> <div class="post-title">Reading a tide table before you go</div> <div class="byline">By Ana Ruiz · <span class="date">9 October 2026</span></div> </div> … <div class="post-tags"> … </div> <div class="comments"> <div class="comment"> … </div> </div> </div> <div class="sidebar"> … </div> </div> <div class="site-footer"> <div class="contact"> … </div> <div class="footer-links"> … </div> </div>
  1. The site header is the page's introductory and navigational aids, scoped to the body: header, which gives the banner.
  2. The three links in it are the site's main navigation: nav, named "Primary," with the links as a ul, since they are a list.
  3. The search box becomes search around a real form; the fake button becomes a button, and the input gets an accessible name.
  4. The content wrapper holds what this page is about: main.
  5. The post is self-contained: article. Its header becomes a header, not a banner inside the article; the title becomes the h1, and the date a time.
  6. The two parts of the post have headings: section, left unnamed.
  7. The tags line is information about the post: the article's footer. It is a short row of links, so no nav.
  8. The comments are a section named by its heading, so it is a region a user can jump to, and each comment is an article nested in the post.
  9. The sidebar is related but separate: aside, inside main, named by its new h2.
  10. The site footer, scoped to the body, is the footer and gives contentinfo. The contact line is the site's contact information, so it is an address.
<header> <p><a href="/">Tide Pool Journal</a></p> <nav aria-label="Primary"><ul> … </ul></nav> <search> <form> <label for="q">Search the journal</label> <input type="search" id="q" name="q"> <button type="submit">Search</button> </form> </search> </header> <main> <article> <header> <h1>Reading a tide table before you go</h1> <p>By Ana Ruiz · <time datetime="2026-10-09">9 October 2026</time></p> </header> … <section><h2> … </section> <footer> … </footer> <section aria-labelledby="comments"> <h2 id="comments">Comments</h2> <article> … </article> </section> </article> <aside aria-labelledby="more"> … </aside> </main> <footer> <address> … </address> <p> … </p> </footer>

The new version has no class attributes and a style sheet that reads like the page: body > header, nav ul, main > article > footer, article article for the comments. Without its inspector strip, it renders pixel for pixel the same as the soup, in light mode and dark, wide and narrow; that was checked with screenshots in Chrome. Everything that changed is invisible.

Try it: read the landmarks yourself

  1. Open landmarks-page.html in Chrome or Edge and open DevTools. In the Elements panel, open the Accessibility pane and turn on "Enable full-page accessibility tree"; the button that appears switches the Elements panel to the accessibility tree. Find the seven landmarks, and find the article's header. What role does it have?
  2. Do the same on div-soup.html. Find the Search "button." What role does the tree give it, and can the Tab key reach it?
  3. Predict, then check with the inspector: in a saved copy of the semantic page, move the page's header inside main. Then remove aria-labelledby from the comments section. Then add aria-label="Archive" to an unnamed section. Which landmarks appear and disappear each time?
  4. List the landmarks of your own most recent project from its source. Is there one main, with the site header outside it?
  5. If you have a Mac, turn on VoiceOver (Command+F5), open the semantic page, and open the rotor (VO+U) to its landmarks list. On Windows, NVDA is a free download; press D to move from landmark to landmark. Then try the soup.

Summary

ConceptKey points
Presentational and semanticPresentational markup says how a thing looks; semantic markup says what it is. Choose the element for the meaning and let CSS decide the look. h1 means "top heading," not "big."
div-itis28.7% of elements on mobile pages are divs (Web Almanac 2024). The standard calls div "an element of last resort." The cure is vocabulary, and it is subtractive.
class-itisPresentational class names, fake elements in classes, classes "just in case." Use the built-in element, select by structure first, and name classes for what content is.
Accessibility treeComputed from the DOM; each node has a role, a name and states. HTML-AAM maps elements to roles. div and span are generic.
Landmarksbanner, navigation, search, main, complementary, contentinfo, plus region and form when named. Users list them and jump between them.
header, footerBanner and content info only when scoped to the body, outside article, aside, main, nav and section. Inside those, they belong to the section and are not landmarks.
mainThe dominant content. One visible per page; ancestors only html, body, div, unnamed form or custom elements.
navMajor navigation blocks only. Name each when there is more than one.
asideTangential content. complementary at page level; inside sectioning content, only when named (Chrome still exposes it unnamed).
section, articleA section is thematic, with a heading, and a region only when named; never a styling wrapper. An article is self-contained and syndicatable; articles nest, as comments in a post.
search, form, addresssearch wraps search and filter controls; Baseline since 2023. A form is a landmark only with a name. address is contact information, role group, not a landmark.
div and spanStill right for layout wrappers, script hooks and styling, when no element means what you need.
The outlineThe sectioning outline algorithm never shipped and was removed in 2022. The outline is the headings, in tree order, at the levels you write.
ARIA rolesA band-aid for markup that cannot change. First rule of ARIA: use the native element. Redundant roles are not recommended.

Next steps

Landmarks divide a page into its large regions. Inside them, the finer structure comes from headings, paragraphs, lists and the text-level elements, and the outline a screen-reader user scans is made of headings alone. Lesson 06, Headings, text and lists, takes that structure down to the word, including the b and strong question this lesson only opened. For the skip link that pairs with main, see Usable links; for how CSS can select all this structure without classes, see Selectors.