Semantic HTML and Landmarks
TL;DR
Semantic HTML is ~80% of accessibility for free. Native elements (<button>, <a>, <nav>, <main>, <h1>–<h6>, <label>, <table>) come with the correct role, keyboard behavior, focusability, and states that assistive tech relies on. A <div onClick> has none of that. Use the right element first; reach for ARIA only to fill genuine gaps. Landmarks (header, nav, main, aside, footer) let screen-reader users jump between page regions; a logical heading hierarchy is how they skim.
In depth
Why prefer <button> over <div onClick>?
A native <button> is, for free:
- focusable (in the tab order) — a
<div>is not withouttabindex="0", - operable by keyboard — Enter/Space activate it; a
<div>needs manualkeydownhandling, - announced as “button” with its label by screen readers — a
<div>is announced as nothing, - equipped with disabled semantics and form participation.
<!-- Faking it, and still not equivalent. -->
<div role="button" tabindex="0"
@click="save"
@keydown.enter="save"
@keydown.space.prevent="save">
Save
</div>
<!-- All of that free, plus disabled and form submit. -->
<button type="submit">Save</button>To match a <button> with a <div> you’d need role="button", tabindex="0", and key handlers for Enter and Space — and you’d still miss edge cases. Just use <button>.
What are landmarks and why do they matter?
Landmark elements define page regions that screen-reader users navigate between directly (e.g., “jump to main”):
| Element | Role | Purpose |
|---|---|---|
<header> |
banner (when top-level) |
site header |
<nav> |
navigation |
navigation blocks |
<main> |
main |
the primary content (one per page) |
<aside> |
complementary |
tangential content |
<footer> |
contentinfo (top-level) |
site footer |
<section> with a name |
region |
named significant area |
<form> with a name |
form |
a form region |
Use one <main>. Give repeated landmarks accessible names so users can tell them apart:
<header>
<nav aria-label="Primary">...</nav>
</header>
<main id="main">...</main>
<aside aria-label="Related articles">...</aside>
<footer>
<nav aria-label="Footer">...</nav>
</footer>Two unlabelled <nav> elements are announced identically, so the landmark list
reads “navigation, navigation” and helps nobody. And <main id="main"> is what
the skip link needs — <a href="#main" class="skip">Skip to content</a> as the
first focusable element on the page.
How should headings be structured?
One <h1> describing the page, then a logical, non-skipping hierarchy (h1 → h2 → h3, don’t jump h2→h4). Headings are the screen-reader equivalent of skimming — users list and jump by heading. Don’t pick a heading level for its font size (style with CSS); pick it for document structure.
When is a <div>/<span> the right choice?
For styling/layout containers with no semantic meaning. <div> and <span> have no implicit role, which is correct when the element is purely presentational. Problems arise only when you put behavior (click handlers, “buttons,” “links”) on them.
What semantic elements are commonly under-used?
<button type="button"> (vs div), <a href> for navigation (vs button/div with JS), <label> tied to inputs, <fieldset>/<legend> for groups, <table> with <th scope> for tabular data, <ul>/<ol>/<li> for lists, <nav>/<main>/<aside>, <details>/<summary> for native disclosure, <dialog> for modals, <time>, <figure>/<figcaption>. Each carries semantics ARIA would otherwise have to fake.
<button> vs <a> — when each?
<a href> navigates (changes URL / location) and should be a link. <button> performs an action in place (submit, toggle, open menu). Misusing them breaks expectations: links open in new tabs, are bookmarkable, and respond to Enter; buttons respond to Enter and Space and aren’t navigable destinations. A “link” with role="button" is usually a sign you picked the wrong element.
Gotchas / edge cases
tabindex> 0 is an anti-pattern — it jumps the element ahead of natural order and creates confusing focus. Use0(in natural order) or-1(focusable only programmatically).role="presentation"/aria-hiddenon interactive elements removes them from the a11y tree but they remain keyboard-focusable — a trap. Don’t hide focusable things.- Headings used for size produce nonsense outlines for screen readers — keep level = structure, size = CSS.
- Multiple
<main>or<h1>confuses landmark/heading navigation. - Generic link text (“click here”, “read more” ×10) is useless out of context — screen-reader users list links; make them self-describing.
<section>without an accessible name is not exposed as a landmark — name it or use a plain element.
What a senior is expected to say 4
- “Semantic HTML gives role, keyboard behavior, focusability, and state for free —
<div onClick>gives none. Right element first, ARIA only for gaps.” - “Landmarks let screen-reader users jump between regions; one
<main>, named duplicate<nav>s.” - “Headings are document structure for skimming — level by meaning, size by CSS, no skipped levels.”
- “
<a>navigates,<button>acts; positivetabindexis an anti-pattern.”
Cross-references
- ARIA for the genuine gaps: ARIA Done Right
- Keyboard & focus behavior you get free with semantic elements: Keyboard and Focus Management
- WCAG 1.3.1 (info & relationships): WCAG and the POUR Principles
- HTML element reference / primer: HTML
Further reading
- MDN — HTML: A good basis for accessibility: https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML
- ARIA Landmarks (W3C WAI): https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/
- HTML spec — Sections: https://html.spec.whatwg.org/multipage/sections.html