Skip to content

Semantic HTML

docs.scrimba.com

Two pages can look identical and be built very differently underneath. One uses elements named after what they hold: a header, a navigation, a main area. The other wraps everything in the same anonymous box and leans on class names to tell them apart. On screen you cannot tell them apart. In every other respect they are not the same page at all. Choosing elements by their meaning is what semantic HTML is about.

What semantic means

Semantic means "to do with meaning". A semantic element is one whose name says what its content is. A paragraph goes in a <p> because it is a paragraph. A navigation menu goes in a <nav> because it is navigation. The tag names the thing.

You could build almost any page out of one all-purpose box and make it look right with styling. But then nothing on the page would say what it is. Picking the element that names the content is like labelling the boxes when you move house. Everything would still fit in plain unlabelled boxes, but the labels are what let anyone else, or a browser, understand what is inside without opening every one.

An element is semantic when its name describes the meaning of its content, not how the content looks. <nav>, <article>, and <button> are semantic: they tell you what the content is for. <div> and <span> are not: they are generic containers that mean nothing on their own.

The payoff is that the meaning lives in the markup, where other tools can read it. A browser knows a <button> is clickable and focusable with the keyboard without you adding anything. A screen reader can announce a <nav> as a navigation region. Search engines treat an <article> as a self-contained piece of content. None of that works if the element is a styled <div>, because a <div> makes no promises about what it holds.

Semantics is the difference between an element that carries meaning and one that only carries pixels. When you write <nav>, you are not just drawing a box, you are stating a fact about the document: this region is navigation. That fact is recorded in the accessibility tree (the parallel structure the browser builds alongside the DOM, describing each element's role and state for assistive technology), and it is what tools other than a sighted mouse user act on.

The practical test is substitution. If you replaced an element with a bare <div> and styled it to look identical, what would you lose? For a <p> or a decorative wrapper, nothing meaningful. For a <button>, you would lose keyboard focus, the Enter and Space key handling, and the "button" announcement to a screen reader, and you would have to rebuild all of it by hand. That gap is the semantics: the behaviour and meaning the browser gives you for free because the element's name told it what the content is. Choosing the named element is not a style preference, it is the cheapest way to get correct behaviour.

JunoWhat semantic means Semantic means the tag is named after what it holds: a paragraph in a <p>, a menu in a <nav>. You could put everything in plain boxes and style it to look the same, but then nothing says what it is. Think of it as labelling your moving boxes so anyone can tell what is inside.
JunoWhat semantic means A semantic element names its meaning, a <div> names nothing. The win is that browsers, screen readers, and search engines can all read the meaning straight off the markup. Reach for the element that describes the content, and keep <div> for when there really is nothing to describe.
JunoWhat semantic means Run the substitution test: swap the element for a styled <div> and ask what you lost. If the answer is keyboard focus, a role in the accessibility tree, or a screen-reader announcement, that is the semantics you were getting for free. Rebuilding it by hand is always more work than using the named element.

The landmark elements

A handful of elements exist to mark out the big regions of a page. These are the ones you reach for first:

  • <header> is the top of a page or a section: a logo, a title, an introduction.
  • <nav> is a set of navigation links, like the main menu.
  • <main> is the main content, the reason the page exists.
  • <section> is a grouped chunk of related content, usually with its own heading.
  • <article> is a self-contained piece that would still make sense on its own, like a blog post or a news story.
  • <aside> is content off to the side: related links, a note, an advert.
  • <footer> is the bottom of a page or section: copyright, contact details, small print.

The rule is the same every time. If a region is navigation, it goes in a <nav>, not a <div>. If it is the footer, it goes in a <footer>. You name the region with the element that matches it.

html
<header>
  <h1>Mara's Kitchen</h1>
  <nav>
    <a href="/recipes">Recipes</a>
    <a href="/about">About</a>
  </nav>
</header>
<main>
  <h2>Weeknight dinners</h2>
  <p>Quick meals you can make after work.</p>
</main>
<footer>
  <p>Made in Lisbon.</p>
</footer>

Seven elements carve a page into its major regions, and together they are called the landmark elements because each one marks a region a reader or a tool can navigate to:

  • <header> introduces its nearest container: at the top of the page it holds the site branding, inside an <article> it holds that article's title and byline.
  • <nav> wraps a block of navigation links. Use it for the significant ones, the main menu or a table of contents, not for every stray link on the page.
  • <main> holds the primary content of the document, the part unique to this page rather than repeated across the site.
  • <section> groups related content that belongs together, normally introduced by a heading.
  • <article> is a complete, independent unit: a post, a comment, a product card. The test is whether it would still make sense pulled out and shown on its own.
  • <aside> holds content tangential to what surrounds it: a sidebar, a pull quote, related links.
  • <footer> closes its nearest container with the wrap-up material: authorship, links, legal text.

The key idea is that most of these are contextual. A <header> and a <footer> describe whatever they sit inside, so a single page can hold several of each, one pair for the page and one for each <article>. <main> is the exception, covered in the next section.

html
<article>
  <header>
    <h2>Roasted tomato soup</h2>
    <p>By Kwame, 20 minutes</p>
  </header>
  <p>Halve the tomatoes and roast them until soft.</p>
  <footer>
    <p>Serves four.</p>
  </footer>
</article>

The seven region elements are the HTML landmarks: elements that expose a navigable region in the accessibility tree. Each maps to a role that assistive technology can list and jump between, which is the whole reason they matter beyond looking tidy in the source.

  • <header> maps to the banner role at the page level, but not when it is nested inside an <article>, <aside>, <main>, <nav>, or <section>, where it is only an introductory group with no landmark role.
  • <footer> mirrors that: contentinfo at the page level, and a plain group when nested inside one of those sectioning elements.
  • <nav> is the navigation role. A page commonly has more than one, a primary menu and an in-page contents list, which is where labelling comes in below.
  • <main> is the main role and marks the primary content. There is one per page.
  • <aside> is the complementary role: content that supports the main content but stands apart from it.
  • <section> becomes a region landmark only when it has an accessible name, normally from a heading tied to it; an unnamed <section> exposes no landmark and is, for the accessibility tree, close to a <div>.
  • <article> carries the article role, a self-contained composition, which tools can enumerate but which is not a navigation landmark in the same sense as the others.

The design goal is a document a non-visual user can survey as fast as a sighted user scans the layout. A screen reader can produce a list of every landmark and let the user jump straight to "main" or "navigation" without hearing the intervening content. That only works if the regions are marked with real landmark elements. Wall-to-wall <div>s produce a document with no landmarks at all, so the only way through it is top to bottom.

html
<body>
  <header>            <!-- banner: page-level -->
    <nav>...</nav>    <!-- navigation -->
  </header>
  <main>              <!-- main: the one primary region -->
    <article>...</article>
    <aside>...</aside> <!-- complementary -->
  </main>
  <footer>...</footer> <!-- contentinfo: page-level -->
</body>
JunoThe landmark elements There are seven region tags worth knowing: <header>, <nav>, <main>, <section>, <article>, <aside>, and <footer>. Each one names a part of the page, and you pick the one that matches the region you are building. You do not have to memorise them today, they come up again and again until they stick.
JunoThe landmark elements Seven elements split a page into regions, and most of them describe whatever they sit inside, so <header> and <footer> can appear more than once. The <article> test that saved me a lot of second-guessing: could this stand on its own if you pulled it out? If yes, it is an article; if it is a grouped chunk with a heading, it is a section.
JunoThe landmark elements These are the landmarks, and each maps to a role a screen reader can jump to, which is why they beat a pile of <div>s. Two catches worth holding onto: <header> and <footer> only become the banner and contentinfo landmarks at the top level, and a <section> is only a landmark once it has an accessible name. An unnamed section is basically a div to assistive tech.

div and span, the non-semantic fallback

Not every part of a page has a meaning worth naming. Sometimes you only need a box to group things so you can style them, or wrap a few words to colour them. For that there are two plain elements with no meaning of their own.

<div> is a generic block: an empty container you use when no other element fits. <span> is the same idea for a small run of text inside a line. Neither says anything about its content. They exist purely as hooks for styling and grouping.

html
<p>Today's total is <span class="price">£42.00</span>.</p>

<div class="card">
  <h2>Special offer</h2>
  <p>Two for one on all pasta.</p>
</div>

The <span> wraps a price so you can colour it. The <div> groups a card so you can put a border around it. Reach for these only when nothing more meaningful fits, because they tell the browser nothing about what is inside.

<div> and <span> are the two elements with no meaning at all, and that is exactly what they are for. A <div> is a generic block-level container; a <span> is a generic inline one. You use them as styling hooks: a place to hang a class or an id when you need to group or target content but there is nothing meaningful to say about it.

html
<div class="grid">
  <article class="card">...</article>
  <article class="card">...</article>
</div>

The <div class="grid"> exists only to lay the cards out in a grid. That is a presentational job, not a meaningful one, so a <div> is the correct choice, not a fallback you should feel bad about. The mistake is the reverse: using a <div> for something that does have meaning. A clickable <div> should be a <button>, a <div> full of nav links should be a <nav>, a <div> wrapping a whole post should be an <article>. When there is no meaning, only a layout or styling need, <div> and <span> are the right answer.

<div> and <span> are the semantically empty elements: a generic block and a generic inline container, respectively, with no role in the accessibility tree and no meaning to any tool. They are the correct choice for one job, a presentational hook: an element that exists solely to attach styling or to group children for layout, and that carries no information a screen reader or search engine should ever announce.

The value of having an element with no meaning at all is that it keeps the meaningful elements truthful. A grid wrapper, a flexbox row, a positioning context: these are real needs with no semantic content, and forcing a meaningful element onto them would be a lie in the markup. A <section> used purely for a background colour claims to be a region it is not. So the rule runs both ways. Do not reach for a <div> when a named element fits, and do not reach for a named element when all you need is a box.

The failure mode to watch for is the interactive <div>: an element given a click handler in JavaScript to act like a button or a link. It renders and it clicks with a mouse, so it looks finished, but it has no role, it is not in the tab order, it does not respond to Enter or Space, and a screen reader announces nothing. Recreating all of that with tabindex, role, and key handlers is a fragile reimplementation of a <button> that already exists. If a thing behaves like a control, use the control element; keep <div> and <span> for the parts that are only ever pixels.

Junodiv and span, the non-semantic fallback<div> is a plain box and <span> is a plain wrapper for a few words, and neither one means anything. Use them when you only need to group or style something and no named element fits. If the thing does have a meaning, like a menu or a button, use the element for that instead.
Junodiv and span, the non-semantic fallback A <div> or <span> is the right call when the only job is layout or styling, with no meaning to state. Do not feel bad about them, feel bad about the reverse: a clickable <div> that should be a <button>, or a wrapper <div> that should be an <article>. Empty element for empty meaning, named element for real meaning.
Junodiv and span, the non-semantic fallback<div> and <span> have no role in the accessibility tree, which is precisely why they are right for pure styling hooks and wrong for anything meaningful. The one that will bite you is the interactive <div> with a click handler: no focus, no keyboard, no announcement, and a pile of tabindex and role patches to fake what <button> gives you for nothing. If it behaves like a control, make it the control.

Choosing the right element

When you are not sure which element to use, ask one plain question: what is this thing? Not what does it look like, but what is it. The answer usually names the element for you. A row of links across the top is navigation, so it is a <nav>. A block of small print at the bottom is a footer, so it is a <footer>. A price you want to colour is not really anything, so it is a <span>.

Start from the meaning, and only fall back to a <div> or <span> when the real answer is "it is only a box". Getting into this habit early means your pages describe themselves, and that pays off the moment you or anyone else has to read the markup again.

Choosing the right element is a short decision you can run every time. Ask what the content is, and let the answer pick the element:

  • Is it its own complete thing that would make sense on its own? <article>.
  • Is it a grouped chunk of related content with a heading? <section>.
  • Is it navigation? <nav>. An introduction to a region? <header>. A wrap-up? <footer>.
  • Is it side content that supports the main content? <aside>.
  • Is there no meaning, only a layout or styling need? <div>, or <span> inside a line.

The trickiest fork is <section> versus <article> versus <div>. Walk it in order. If the block could stand alone, it is an <article>. If not but it is a themed group with its own heading, it is a <section>. If it has no heading and no meaning, and exists only to be styled or laid out, it is a <div>. A <section> should almost always have a heading; if you cannot think of one, that is a strong hint you wanted a <div>.

html
<main>
  <section>
    <h2>Latest posts</h2>
    <article>
      <h3>Why I switched to plain HTML</h3>
      <p>...</p>
    </article>
    <article>
      <h3>The case against heavy frameworks</h3>
      <p>...</p>
    </article>
  </section>
</main>

Here the <section> is a themed group, "latest posts", and each post inside it is an <article> because it stands on its own. The nesting mirrors the meaning.

Choosing well comes down to reading the meaning first and the layout never, but a few rules make the common decisions mechanical.

There is exactly one <main> per page. It marks the primary content, the part unique to this page rather than the header, nav, and footer repeated across the site. More than one <main> in the accessibility tree gives a screen-reader user two "main" targets and no way to know which is the real one, so keep it singular. Everything site-wide belongs outside it.

The other rule that repeatedly matters is labelling repeated landmarks. When a page has two elements of the same landmark type, a primary <nav> and an in-page table of contents, both announce as "navigation" and the user cannot tell them apart. You disambiguate with aria-label, an attribute that supplies a short accessible name for a region without showing anything on screen:

html
<nav aria-label="Primary">
  <a href="/">Home</a>
  <a href="/blog">Blog</a>
</nav>

<nav aria-label="On this page">
  <a href="#intro">Introduction</a>
  <a href="#setup">Setup</a>
</nav>

Now the two regions announce as "Primary navigation" and "On this page navigation", and the landmark list is usable. Apply the same labelling to any repeated landmark, <aside> or <section> included. For <section>, remember it only becomes a landmark once it has an accessible name at all, whether from an associated heading or an aria-label.

The meta-rule behind all of this: the markup is a machine-readable description of the document, read by screen readers, search crawlers, reader-mode tools, and your future self. Choosing elements by meaning is what makes that description true. Layout is CSS's job, and any time you find yourself picking an element for how it looks, you have crossed into CSS's lane using the wrong tool.

JunoChoosing the right element When you are stuck, ask what the thing is, not what it looks like. Navigation is a <nav>, small print is a <footer>, a plain box you want to style is a <div>. Start from the meaning every time, and your pages end up describing themselves.
JunoChoosing the right element Run the little checklist: stands on its own is an <article>, themed group with a heading is a <section>, no meaning and only styling is a <div>. The tie-breaker that settles most section-versus-div arguments: if you cannot name a heading for it, you wanted a <div>. Let the meaning pick the tag and the nesting sorts itself out.
JunoChoosing the right element Two rules carry most of the weight: exactly one <main> per page, and label repeated landmarks with aria-label so two navs do not both say "navigation". Keep in mind a <section> is only a landmark once it has an accessible name. Pick elements by meaning and leave the looks to CSS, because the moment you choose a tag for how it renders, you are doing CSS's job with the wrong tool.