// guide
Semantic HTML: Choosing the Right Elements
Semantic HTML is the single most impactful thing you can do for accessibility. The right elements give you keyboard support, screen reader announcements, and search engine understanding with zero extra code. This guide covers the elements that matter most and when to use each one.
// 01 · why semantics matter
Why Semantics Matter
Semantic HTML means using elements for their intended purpose: <button> for actions, <nav> for navigation, <h2> for section headings. When you use the right element, the browser does the hard work for you.
Screen readers use semantic HTML to navigate. Headings become a table of contents. Landmark elements (<nav>, <main>, <aside>) become jump points. Lists announce their item count. Without semantics, assistive technology users get a flat wall of text with no structure.
Built-in keyboard interaction comes free. A <button> is focusable, responds to Enter and Space, and announces its role. A <div onclick> does none of that unless you manually add tabindex, key event listeners, and ARIA roles. Native elements also handle focus management and state announcements (checked, expanded, selected) without any JavaScript.
Search engines benefit too. Crawlers use headings, landmarks, and document structure to understand page content. Semantic HTML improves SEO alongside accessibility.
Maintainability improves. Semantic markup is self-documenting. A developer reading <nav aria-label="Main"> immediately understands the purpose. A developer reading <div class="nav-wrapper"> has to check the CSS and JavaScript to understand what it does.
// 02 · landmark elements
Landmark Elements
Landmarks are structural elements that divide a page into meaningful regions. Screen readers let users jump directly between landmarks, skipping over content they don't need. Think of them as a page-level navigation system that assistive technology provides for free.
| Element | Implicit ARIA Role | When to Use |
|---|---|---|
<header> |
banner |
The site-wide header containing logo, navigation, and global actions. One per page (as a direct child of <body>). |
<nav> |
navigation |
A group of navigation links. Use for primary nav, footer nav, breadcrumbs, and table of contents. |
<main> |
main |
The primary content of the page. Exactly one per page. Skip links typically target this element. |
<aside> |
complementary |
Content tangentially related to the main content, such as sidebars, related links, or pull quotes. |
<footer> |
contentinfo |
The site-wide footer containing copyright, legal links, and secondary navigation. One per page (as a direct child of <body>). |
<section> |
region (when labeled) |
A thematic grouping of content. Only becomes a landmark when given an accessible name via aria-labelledby or aria-label. |
<article> |
article |
A self-contained piece of content that could stand alone: a blog post, a comment, a product card, or a forum thread. |
<nav> elements need labels
When a page has more than one <nav>, screen readers list them all as "navigation." Users can't tell them apart unless you label each one with aria-label. This site uses <nav aria-label="Main navigation"> in the header and <nav aria-label="Breadcrumb"> for the breadcrumb trail — screen reader users hear the difference immediately.
// 03 · heading hierarchy
Heading Hierarchy
Headings (<h1> through <h6>) create a document outline that screen readers expose as a navigable table of contents. Users can list all headings on a page, jump to any heading, or skip between headings at the same level. This makes headings one of the primary ways assistive technology users navigate.
The rules
- One
<h1>per page — this is the page title. It tells users and search engines what the page is about. - Don't skip levels — go from
<h2>to<h3>, never from<h2>to<h4>. Skipping levels breaks the outline and confuses screen reader users who rely on heading structure to understand content relationships. - Headings are not for styling — if you need big bold text, use CSS. Using an
<h3>because it looks the right size creates a misleading document structure.
How screen readers use headings
Most screen readers let users press a single key to jump to the next heading (typically H in NVDA and JAWS). Users can also press 1 through 6 to jump to headings at a specific level. On a well-structured page, a user can press H a few times to find the section they want, then start reading. On a poorly structured page, they have to listen to everything linearly.
- Using
<div class="heading">instead of<h2>— a styled<div>is invisible to screen readers. Users navigating by heading will skip right past it. - Skipping
<h2>to use<h3>for visual size — this tells assistive technology that the<h3>is a subsection of a non-existent<h2>. Use CSS to style headings at any size you need. - Using headings purely for bold text — headings create structural landmarks. If you just need bold text, use
<strong>or CSSfont-weight.
// 04 · interactive elements: native vs custom
Interactive Elements: Native vs Custom
Native HTML interactive elements come with built-in keyboard support, focus management, ARIA roles, and state announcements. Custom implementations using <div> and <span> require you to recreate all of that manually — and the results are almost always worse.
| Need | Use This | Not This | What You Get for Free |
|---|---|---|---|
| Click action | <button> |
<div onclick> |
Focus, Enter/Space activation, role="button", disabled state |
| Navigate to a URL | <a href> |
<span onclick> |
Focus, Enter activation, role="link", right-click context menu, middle-click to open in new tab |
| Expand/collapse | <details>/<summary> |
Custom div toggle | Focus, Enter/Space, expanded/collapsed state announcement, no JavaScript required |
| Selection from a list | <select> |
Custom dropdown | Focus, arrow key navigation, type-ahead search, role="listbox", mobile-optimized picker |
| Toggle on/off | <input type="checkbox"> |
<div> with ARIA |
Focus, Space to toggle, checked/unchecked state, works with <label> |
When a native element isn't enough — for example, a tabbed interface or a modal dialog — follow the established patterns and lean on ARIA to fill the gaps. See these patterns for accessible implementations:
- Accordion — uses
<details>/<summary>for native expand/collapse - Modal Dialog — uses the
<dialog>element for native modal behavior - Accessible Forms — demonstrates native form controls with proper labeling
// 05 · lists and structure
Lists and Structure
Lists are more than a visual convention. Screen readers announce list structure — "list, 5 items" — giving users context about how much content is in a group and how it's organized. Without list markup, a series of items is just disconnected text.
<ul>for unordered lists — navigation menus, feature lists, tag groups. Order doesn't matter. This site's own nav menus use<ul>inside<nav>.<ol>for ordered lists — step-by-step instructions, rankings, table of contents. Order is meaningful. The breadcrumb and table of contents on this page both use<ol>.<dl>for description lists (key-value pairs) — glossaries, metadata, FAQs. Each<dt>is a term, each<dd>is its description.
<ul> with five <li> children, it announces "list, 5 items" and users know exactly how much content to expect. With plain <div> elements, they get no count and no indication that the items are related.
// 06 · tables
Tables
HTML tables are for tabular data — information organized into rows and columns where the relationships between cells matter. Screen readers use table markup to let users navigate by row and column, announcing the relevant headers as they move. Without proper table structure, data becomes a meaningless stream of text.
Required table markup
<caption>(oraria-label) — gives the table an accessible name so users know what data it contains before reading it.<thead>with<th scope="col">— marks column headers so screen readers can announce them as users navigate cells in that column.<th scope="row">— marks row headers so screen readers announce them as users navigate across a row.
See the Data Table pattern for a full implementation with sorting, responsive behavior, and screen reader testing results.
<table>. Always. Visual styling can be applied to real table elements with CSS.
// 07 · form markup
Form Markup
Forms are where accessibility most visibly breaks down. Missing labels, unlabeled groups, and wrong input types create barriers for screen reader users, keyboard users, and mobile users alike. The right HTML elements solve most of these problems.
<label>withforattribute — every form control needs a label, and theforattribute must match the input'sid. This lets screen readers announce the label when the input is focused, and lets mouse users click the label to focus the input.<fieldset>/<legend>— groups related inputs (radio buttons, checkboxes, address fields) and announces the group label before each input. Without this, screen readers announce "Yes" and "No" with no indication of what question they answer.<input type>— using the correct type (email,tel,url,number,date) triggers the appropriate keyboard on mobile devices and enables built-in validation. An<input type="email">shows the @ key on iOS; a generic<input type="text">does not.<output>— represents the result of a calculation or user action. Screen readers announce it as a live region, so users hear updated results without manually navigating to them.
For a complete treatment of accessible form patterns — including validation, error messages, and multi-step forms — see the Accessible Forms pattern.
// 08 · next steps
Next Steps
Semantic HTML is the foundation every other accessibility technique builds on — get the elements right and most of WCAG follows for free. To verify your markup choices in a real codebase, work through the Developer Accessibility Checklist, which maps each check to a WCAG success criterion. When native elements genuinely fall short, the ARIA guide covers when — and when not — to reach for ARIA.