Semantic HTML: using the element that means the thing

A page built from divs and a page built from headings, lists, tables and landmarks can look identical. Only the second can be navigated by heading, jumped to by landmark, read as a table, or understood by a search engine. Semantic HTML is choosing the element that means what the thing is.

Semantic HTML means using the element that says what a piece of content is: an <h2> for a heading rather than a bold div, a <table> with header cells for a table rather than a grid of boxes, <nav>, <main> and <article> for the regions of the page.

The markup. The highlighted line is the part this term is about.
The markup. The highlighted line is the part this term is about.

The page looks the same either way. The difference is what a screen reader can announce, what a keyboard can jump to, what a search engine can understand, and what the next person to edit the markup can read.

This guide covers what the real element gives you, the substitutions worth making, and the ones that do not matter.

<!-- looks like a button -->
<div class="btn" onclick="save()">Save</div>

<!-- is a button -->
<button type="button" onclick="save()">Save</button>

Identical on screen. The second one gets six behaviours the first does not.

What the real element gives you in semantic HTML

Behaviour <button> Styled <div>
Reachable by Tab Yes No
Fires on Enter and Space Yes No
Announced as "button" Yes Announced as nothing
Disabled state disabled attribute Hand-rolled
Submits a form Yes No
Focus ring Provided Hand-rolled
Semantic HTML uses the element that says what a thing is, a heading, a nav, a table with headers, instead of a styled div. It looks the same and it means something to screen readers, search engines and anyone reading the markup.
Semantic HTML uses the element that says what a thing is, a heading, a nav, a table with headers, instead of a styled div. It looks the same and it means something to screen readers, search engines and anyone reading the markup.

Reproducing those on a div takes tabindex, a role, two key handlers, an aria-disabled attribute and a focus style — about fifteen lines to arrive back where <button> started.

A page split across files ✗ Works only inside its own folder ✗ Styling vanishes when sent alone ✗ Images turn into empty boxes ✗ Breaks the moment a file is renamed One self-contained file ✓ Renders anywhere it lands ✓ Styling travels with it ✓ Images are carried inside ✓ Nothing to keep together
A page whose structure is in its elements carries that structure wherever it goes. One whose structure is only in its CSS loses it the moment the CSS does not apply.

The substitutions worth making

Instead of Use What you gain
<div class="btn"> <button> The six rows above
<div class="link"> <a href> Middle-click, open in new tab, right-click menu
<div class="nav"> <nav> "Skip to navigation" for screen readers
<div class="table"> <table> Row and column announcement
<div class="header"> <header> Landmark navigation
<b> for emphasis <strong> Announced with emphasis
<div> list of items <ul><li> "List, four items"
<div class="accordion"> <details><summary> Works with no JavaScript at all
<span onclick> toggle <input type="checkbox"> Keyboard, state, form value

The <details> row is the most underused. An expandable section with no script:

<details>
  <summary>Does the viewer run JavaScript?</summary>
  <p>Yes. Charts and calculators behave as they would in a browser tab.</p>
</details>

Keyboard accessible, announced correctly, and searchable by the browser's find-in-page — which a script-driven accordion with hidden content is not.

Headings are structure, not sizes

<!-- wrong: chosen for appearance -->
<h4>How it works</h4>

<!-- right: chosen for level, sized with CSS -->
<h2 class="small">How it works</h2>

Screen reader users navigate by jumping between headings, so the levels are a table of contents. Skipping from h2 to h4 reads as a missing section. Use one h1, then h2 for sections and h3 beneath them, and set the sizes in CSS.

Tables need their parts

<table>
  <caption>Launch checklist</caption>
  <thead>
    <tr><th scope="col">Task</th><th scope="col">Owner</th></tr>
  </thead>
  <tbody>
    <tr><td>Pricing page</td><td>Jae</td></tr>
  </tbody>
</table>

thead, tbody and scope="col" are what let a screen reader announce "Owner, Jae" rather than reading an undifferentiated stream. A table built from divs looks identical and is unusable without sight. See sharing a table.

Labels, properly

<!-- placeholder only: disappears when typing starts -->
<input type="email" placeholder="Email">

<!-- a real label -->
<label for="email">Email</label>
<input id="email" type="email" autocomplete="email">

A real label is announced with the field, and tapping it focuses the field — which on a phone makes the target larger. See turning a form into a link for the rest of the form details.

ARIA is the fallback, not the default

<!-- unnecessary -->
<button role="button">Save</button>

<!-- necessary, because HTML has no element for this -->
<div role="tablist">…</div>

The first rule of ARIA is to not use ARIA when a real element exists. It is for patterns HTML does not have — tabs, live regions, complex widgets. See aria-label.

Why it is worth the attention

The accessibility argument is the strongest one. The practical one is that semantic markup is less code: every behaviour you do not have to reimplement is code you do not have to maintain, and browser-provided behaviour is more correct than hand-rolled behaviour on devices you have never tested.

Three substitutions that matter most

A bold paragraph for a heading. Screen readers list headings for navigation; a bold paragraph is not on the list. Use h2 and style it.

Divs in a grid for a table. A table of numbers built from divs cannot be read row by row with its column headers. Use table, thead, th with scope="col".

A styled div for a button. It is not focusable, not announced as a button, and does not respond to Enter. Use button.

What generated HTML gets wrong here

Assistants often produce headings and tables correctly, and can produce clickable divs and bold-paragraph headings when asked for something that sounds visual.

A quick pass with the headings list in an accessibility inspector, or a screen reader's heading navigation, shows whether the structure is in the elements or only in the CSS. The ten-problem checklist has it as one of the checks.

Making a page semantic: 4 steps

  1. Use headings in order for the structure. One h1, h2 for sections, h3 inside them. Do not skip a level to get a smaller size; size is CSS.
  2. Mark the regions. header, nav, main, article, footer. A screen reader can jump between them; a crawler knows which part is the content.
  3. Use real tables and lists for tabular and listed content. thead, th with scope, tbody; ul or ol for lists. Not divs styled to look like them.
  4. Reserve div and span for grouping and styling. They mean nothing, which is exactly right when nothing needs to be meant. aria-label covers the cases where a real element still needs a name.

Questions people ask

What is semantic HTML?

Using the element whose meaning matches the content — button for buttons, nav for navigation, table for tabular data — rather than a styled div.

Why does it matter if it looks the same?

Because keyboard focus, screen reader announcement, form submission and browser find-in-page all depend on the element, not the styling.

Does it help search rankings?

Modestly, by making the structure clear. The stronger argument is that the page works for people using a keyboard or a screen reader.

Do I need ARIA attributes as well?

Much less than people assume. A real button needs no role. ARIA is for filling gaps HTML does not cover.

Keep reading

div vs span: which one to use, and whydiv is a block box for layout, span is an inline box for a few words. See the layout boxes iHTML validator: what it checks, and why your page looked fine anywayWhat the W3C Nu HTML validator checks, the errors it finds most often, and how browsers quieThe h2 tag and h3 to h6: heading levels that make an outlineHow to use the h2 tag and h3 to h6 under it: picking a level from its place in the page, subThe section tag in HTML: section vs article vs divWhen to use the HTML section tag, and when article or a div fits better. How a section becomThe aside tag in HTML: sidebars, callouts and related linksWhat the HTML aside tag is for, how it differs from section and div, and how to build a sideThe strong tag in HTML: strong vs b, and bold text with CSSThe strong tag marks important text, b only draws the eye, and both look bold. Live examplesThe main tag in HTML: what goes inside, and where it goesWhat the HTML main tag is for, the one-per-page rule, where it may not go, main vs div and sThe HTML figure tag and figcaptionHow the HTML figure tag and figcaption work: caption position, the default side margins, capThe HTML blockquote tag, and how to style a quoteHow to mark up a quote with blockquote, q and cite, where to put the source, and how to replThe nav tag in HTML: when to use it and how to label itWhat the HTML nav tag does, when to use it and when a div or footer is enough. Label severalThe HTML span tag: styling part of a lineThe span tag wraps part of a line so CSS and JavaScript can reach it. Live examples of span The HTML hr tag, and how to style the lineWhat the hr tag means, why its default line is a grey inset border, and how to set colour, tThe h1 tag in HTML: headings, levels and how to size themWhat the h1 tag means, how h1-h6 build a page outline, why the level is not the size, and hoaria-label: naming things that have no visible textaria-label gives an element a spoken name when nothing visible does: an icon-only button, a tabindex in HTML: keyboard focus, and the value never to usetabindex in HTML controls whether an element is in the keyboard's Tab order. 0 adds an elemeAlt text in HTML: what to write, and when to write nothingAlt text in HTML is what stands in for an image when it cannot be seen: read aloud by a screA sortable HTML table that reads on a phoneA sortable HTML table in a few lines, with tabular numbers, real table markup, phone-first sHTML link to a PDFHow to write an HTML link to a PDF, control whether it opens in the browser or downloads, anHTML link to a section of a pageHow to write an HTML link to a section of a page, from another page or a chat message. Cover