Skip to main content

Semantic HTML and Accessibility

Long before screen readers, people built the web with plain HTML: headings, paragraphs, lists, links, tables. That design turned out to be a gift. Because each element says what it is, software can help people in ways the author never planned: reading aloud, jumping between sections, reformatting, translating, or listing every link.

When you use the right element, you get accessibility almost for free. When you pile up generic div and span elements and style them to look right, you throw that gift away. This lesson shows what you gain by choosing well, and what you lose by not.


What You Will Learn​

  • How people navigate by structure, rather than reading top to bottom
  • Writing a clean heading outline
  • Using landmarks to mark the regions of a page
  • Using the right element for lists, tables, buttons, and links
  • Adding a skip link
  • Keeping the reading order and the visual order together
  • Hiding text visually but not from screen readers

The Big Idea​

index.html
<body>
<a href="#main" class="skip-link">Skip to main content</a>

<header>
<p>The Bread Bin</p>
<nav aria-label="Main">
<ul>
<li><a href="/recipes">Recipes</a></li>
<li><a href="/shop">Shop</a></li>
</ul>
</nav>
</header>

<main id="main">
<h1>Sourdough for Beginners</h1>
<h2>Ingredients</h2>
<ul>
<li>Flour</li>
<li>Water</li>
<li>Salt</li>
</ul>
<h2>Method</h2>
<p>Mix, rest, shape, and bake.</p>
</main>

<footer>
<p>Β© 2026 The Bread Bin</p>
</footer>
</body>

Load each sample, and look at the three lists. These are the same lists that a screen reader can offer. A "div soup" page gives a reader nothing to jump between. The semantic page gives them a table of contents, a map of regions, and a list of links.

How People Really Navigate​

Sighted people scan a page: they glance at the big text, spot the menu, and skip to what they want. Screen reader users do the same thing, but through the code. According to surveys of screen reader users, headings are the most common way to find information, followed by landmarks and links. They rarely listen to a page from top to bottom.

Common navigation shortcuts in screen readers:

Shortcut ideaWhat it needs from your HTML
Jump to the next headingReal h1 to h6 elements
Jump by heading levelHeadings in a logical order
Jump to a landmark (menu, main, footer)header, nav, main, aside, footer
List all linksLink text that makes sense alone
Jump to the next list, table, form field, buttonThe real elements

If your page does not offer these things, a screen reader user has to listen to everything, in order, every time.

Headings: The Outline of Your Page​

Headings are the single most useful structure on a page.

<h1>Sourdough for Beginners</h1>
<h2>Ingredients</h2>
<h2>Method</h2>
<h3>Mixing</h3>
<h3>Shaping</h3>
<h2>Baking tips</h2>

Rules of thumb:

  1. One h1, that names the page. (More than one is allowed by HTML, but one is clearer, and is the usual guidance.)
  2. Do not skip levels. Go from h2 to h3, not from h2 to h4.
  3. Choose the level for meaning, not for size. If a heading is too big, change it in CSS.
  4. Never use a heading just for styling. Do not make bold text a heading to make it look big, and do not use a div to look like one.
  5. Write headings that make sense alone, because people read them as a list.
  6. Do not leave a heading empty.
The outline test

Read only the headings of your page, from top to bottom. Do they tell the story? If not, a reader jumping through them will be lost.

Landmarks: The Map of Your Page​

Landmarks mark the large regions of a page, so people can jump straight to them.

HTML elementLandmark roleUse
<header> (directly in body)bannerThe site-wide header: logo, site name
<nav>navigationA group of navigation links
<main>mainThe main content. One per page
<aside>complementaryRelated content, such as a sidebar
<footer> (directly in body)contentinfoThe site-wide footer
<section> with a nameregionA significant section that you have named
<form> with a nameformAn important form
<div role="search"> or <search>searchA search area

Important details:

  • A header or footer inside an article or section is not a page-level landmark. It belongs to that part.
  • If you have more than one nav, give each one a label, so users can tell them apart: <nav aria-label="Main">, <nav aria-label="Footer">.
  • Do not label things with the landmark's own name. "Main navigation" is fine as a label on a nav. Do not write "navigation" inside the label, because the screen reader already says it.
  • Put all page content inside a landmark, so nothing is left outside the map.
  • Do not overdo it. A page with 20 landmarks is as hard to use as one with none.

For more on these elements and how to lay out a whole page, see Semantic Page Layout.

On most sites, every page begins with the same menu. A sighted mouse user ignores it. A keyboard user must tab through every link before reaching the content, on every page. A skip link fixes that. It is the first link on the page, and it jumps over the repeated blocks.

<a href="#main" class="skip-link">Skip to main content</a>
...
<main id="main" tabindex="-1">...</main>
.skip-link {
position: absolute;
left: -9999px; /* hidden off screen until focused */
}
.skip-link:focus {
position: static; /* appears when a keyboard user reaches it */
padding: 0.5rem 1rem;
background: #fde68a;
color: #1e293b;
}

This is directly related to WCAG 2.4.1 (Bypass Blocks). Use the test below. First try it without the skip link, and count the stops. Then try again with it.

skip-link.html
<body>
<a class="skip-link" href="#main">Skip to main content</a>
<nav aria-label="Main"> ...nine links... </nav>
<main id="main" tabindex="-1">
<h1>Welcome</h1>
<button type="button">Order bread</button>
</main>
</body>

The tabindex="-1" on main lets script and fragment links move focus to it, without adding it to the normal Tab order.

Lists, Tables, and Other Structure​

If your content is…UseBecause
A set of related itemsul, olA screen reader announces "list, 5 items", and lets people jump or skip it
Steps in orderolThe numbering is meaningful, and is announced
Terms and definitionsdl, dt, ddThe relationship is in the code
Data in rows and columnstable, with thHeaders are announced with each cell. See Accessible Tables
A group of related form fieldsfieldset, legendThe group name is announced with each field
A quotationblockquote, qIt is announced as a quotation
Emphasisem, strongThey carry meaning. i and b are only visual
A date or timetimeMachines can read it
An abbreviationabbrA title can explain it

Do not use a table for layout. Screen readers will announce data table structure that is not there. Use CSS grid or flexbox.

Do not use <br> to make lists or spacing. A row of lines and dashes is not a list. Use real list elements, and CSS for the gaps.

This is one of the most important decisions in HTML:

It…UseWhy
Goes to another page or place<a href="…">Links work with the keyboard, open in new tabs, and are announced as links
Does something on this page (open, submit, toggle)<button type="button">Works with Enter and Space, and is announced as a button

If it looks like a button but takes you to another page, it can still be a link, styled like a button. If it opens a menu on the current page, it is a button. Mixing them up confuses both screen reader users and keyboard users. See Accessible Buttons and Accessible Links.

Reading Order and Visual Order​

Assistive technology reads your page in source order, the order of the HTML. CSS can move things around on the screen, using order, flex-direction: row-reverse, grid placement, or absolute positioning. If the visual order and the source order disagree, a keyboard user's focus can jump around the screen unpredictably, and a screen reader reads things in a confusing order.

Guidelines:

  • Write your HTML in the order you want it read. Then use CSS to place it.
  • Test by tabbing. The focus should move in a sensible visual path.
  • Avoid positive tabindex values (such as tabindex="3"). They override the natural order, and are very hard to maintain. Use 0 or -1 only.
  • Avoid reordering interactive content in ways that make a mismatch.

Visually Hidden Text​

Sometimes you want text that screen readers can read, but sighted users do not need: for example, an extra word for an icon, or a heading for a region. Use a visually hidden class:

.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
<a href="/cart">
<svg aria-hidden="true" focusable="false"> ... </svg>
<span class="visually-hidden">Shopping cart, 3 items</span>
</a>

Know the difference between ways of hiding:

MethodVisible?Read by screen readers?In the Tab order?
display: none, visibility: hidden, the hidden attributeNoNoNo
aria-hidden="true"YesNoYes (still focusable!)
.visually-hiddenNoYesYes, if it is a control

Be careful with aria-hidden on anything focusable. See the aria-hidden lesson.

The Page Language and Title​

Two small things at the top of the page have a big effect:

<html lang="en">
<head>
<title>Sourdough for Beginners | The Bread Bin</title>
</head>

The lang attribute lets a screen reader choose the right voice. The title is the first thing announced when a page loads. For more, see The HTML Element and The Title Element.

Good Habits​

  1. Choose the element that matches the meaning.
  2. One h1, no skipped levels, and headings that tell the story.
  3. Wrap content in landmarks, and label repeated ones.
  4. Add a skip link to every page that has a repeated menu.
  5. Use lists for lists and tables for tables.
  6. Use button for actions and a for navigation.
  7. Keep source order the same as visual order.
  8. Use div and span only when no meaningful element fits.
  9. Test with the keyboard and a screen reader.

Accessibility Checklist​

  • The page has a lang, and a unique, meaningful <title>.
  • There is a logical heading outline, with no skipped levels and no empty headings.
  • main appears once, and header, nav, and footer are used where they fit.
  • Repeated landmarks, such as two nav elements, have different labels.
  • A skip link is the first focusable element.
  • Lists use ul, ol, and dl. Tables are used only for data.
  • Buttons are button, and links are a with an href.
  • Tab order follows the visual order, and no tabindex is above 0.
  • Text meant only for assistive technology uses a visually hidden class.
  • No heading is used only for its size.

Security and Privacy Notes​

  • Do not put sensitive data in visually hidden text, or in headings and landmark labels. Anyone using assistive technology, anyone reading the page source, and search engines can see it.
  • Hidden text is also content. Do not use it to stuff keywords for search engines. It can be penalised, and it harms screen reader users.
  • Be careful with user-generated headings. If visitors can post content, make sure they cannot inject their own h1, landmark roles, or aria-hidden. Escape their text, and control which elements are allowed.
  • A skip link target should not be user-controlled. Build href="#main" yourself, and avoid passing arbitrary fragments from the address.
  • Do not hide security warnings with aria-hidden, or off-screen. Anything people need to see for safe use must be available to everyone.

Global Audience Notes​

  • Screen readers read the lang attribute, so a Hindi page set as English will be read with the wrong voice. Set lang on the page and on passages in other languages.
  • Heading and landmark labels should be in the page's language. An English aria-label on a Spanish page is read with an English or Spanish voice, and either is confusing.
  • Right-to-left pages need dir="rtl". The reading order in the code should match the visual order in that direction.
  • Write short, plain headings that translate well, and that fit on small screens in any script.
  • Many people use older phones and basic screen readers. Plain, semantic HTML works best on all of them.
  • Do not use a symbol or emoji alone as a heading or label. Meanings differ across cultures.

Common Mistakes to Avoid​

  1. Div soup: a page with no headings, landmarks, or lists.
  2. Skipping heading levels because of the size it gives.
  3. Using bold or a large font instead of a real heading.
  4. Several main elements, or none.
  5. Unlabelled repeated nav elements.
  6. No skip link on pages with long menus.
  7. A div or span for a button, or an a with no href for an action.
  8. Layout tables.
  9. Visual order that disagrees with the source order.
  10. Positive tabindex values.
  11. aria-hidden on content that can be focused.
  12. Hiding with display: none content that screen readers need.

Practice Time​

Try these in your browser and your editor:

  1. Use the structure navigator, load the div soup sample, and fix it one problem at a time.
  2. Write the heading outline for your own page, then read only that list.
  3. Add a skip link to a page, and test it with the keyboard.
  4. Replace three div elements with header, main, and footer.
  5. Give two nav elements different labels.
  6. Turn a list written with <br> into a real ul.
  7. Find a div that acts as a button, and replace it with a button.
  8. Use CSS to reverse the visual order of two blocks, and then press Tab. What happens?
  9. Make an icon-only link accessible with a visually hidden label.
  10. Bonus: open a news site, and list the headings and landmarks it uses, with your browser's Accessibility panel.

Quick Recap​

Click a question to check your answer.

1. Why does semantic HTML help accessibility?

Each semantic element says what it is, so browsers and assistive technology can announce it, let people jump between elements, and provide keyboard behaviour, without extra code.

2. What are the rules for a good heading outline?

One h1 that names the page, no skipped levels, levels chosen for meaning rather than size, and headings that make sense on their own.

3. What are landmarks?

Regions such as banner, navigation, main, complementary, and contentinfo, created by elements like header, nav, main, aside, and footer. They let users jump straight to a region.

4. What does a skip link do?

It is the first link on a page, and lets keyboard users jump over repeated content, such as a long menu, straight to the main content.

5. When do you use a button, and when a link?

A button does something on the current page. A link goes somewhere else, with an href.

6. How does aria-hidden="true" differ from display: none?

display: none hides the element from everyone, including screen readers, and removes it from the Tab order. aria-hidden hides it only from assistive technology, but a focusable element inside it can still be reached by keyboard, which is a trap.

Key Takeaways​

  • People navigate by structure: headings, landmarks, links, lists, and form fields.
  • Build a clean heading outline, with one h1 and no skipped levels.
  • Use landmarks to map the page, and label repeated ones.
  • Add a skip link so keyboard users can bypass repeated menus.
  • Use the right element: lists for lists, tables for data, button for actions, a for navigation.
  • Keep source order and visual order together, and avoid positive tabindex.
  • Use a visually hidden class for text that is only for assistive technology, and know the difference from aria-hidden and display: none.
  • Language and title are the first things a screen reader announces. Set both.

Next, hear how all this structure sounds with Screen Readers.