Text elements and why semantics matter
You can build any page out of <div> and <span> with enough CSS. It will look
identical. This lesson is about why you should not, and the argument is not about
tidiness.
The page that looks right and is unusable
<div class="title">Sharma Kirana</div>
<div class="subtitle">Fresh groceries in Kothrud</div>
<div class="text">We have been here since 1998.</div>
<div class="link" onclick="go()">See our prices</div>
With CSS, that looks exactly like a proper page. And:
- A screen reader user cannot jump between headings, because there are none. They must listen to the whole page in order. Most screen reader users navigate a new page by pulling up a list of its headings — that list is empty here.
- The "link" cannot be reached by Tab, cannot be opened in a new tab, is not purple after being visited, and does not appear to a search engine as a link at all.
- Reader modes, translation tools and "print this page" all produce rubbish.
- Nothing in the markup tells you what the page is about.
Semantic HTML means choosing elements for what the content means, not what it looks like. You get behaviour, accessibility and meaning for free, and the cost is learning about fifteen element names.
Headings
<h1>Sharma Kirana</h1>
<h2>Our prices</h2>
<h3>Atta and rice</h3>
<h3>Oil and ghee</h3>
<h2>Delivery</h2>
Headings form an outline, and the rules are short:
One <h1> per page, saying what this page is. Not the site name on every
page — About Sharma Kirana on the about page.
Do not skip levels. h1 then h3 leaves a screen reader user wondering what
they missed. Going back up is fine: h3 then h2 starts a new section.
Choose the level for the structure, not the size. If your h2 is too big,
that is CSS's problem, and you will fix it in one line in module 2. Picking h4
because it looked right is how outlines get destroyed.
Test it in devtools: the Elements panel has an Accessibility tab, and Chrome can show you the full heading tree. If it reads like a sensible table of contents, you are done. If it reads like nonsense, so does the page to anybody using a screen reader.
Paragraphs, and the line-break trap
<p>We have been here since 1998.</p>
<p>Delivery is free above ₹500.</p>
<!-- wrong -->
<p>We have been here since 1998.<br><br>Delivery is free above ₹500.</p>
The second gives the same visual result and is wrong: those are two paragraphs, and you have told the browser they are one. A screen reader reads them as a single run-on. You also cannot now style the gap between them, because there is no gap — there are two invisible characters.
<br> is for a line break inside one block of text where the break is part of
the content. An address, a poem, a bit of dialogue:
<p>
Sharma Kirana<br>
12, Karve Road<br>
Kothrud, Pune 411038
</p>
Never use <br> for spacing. Spacing is margin.
Emphasis: the four that get confused
<em>really</em> <!-- stress emphasis. Read with emphasis aloud. Italic -->
<strong>Warning</strong> <!-- strong importance. Bold -->
<i>Rangoli</i> <!-- different in kind: a term, a foreign word, a thought -->
<b>Atta</b> <!-- draw attention, no extra importance -->
<em> and <strong> carry meaning and a screen reader can convey them.
<i> and <b> are for text that is set apart without being more important — a
taxonomic name, a foreign phrase, a product name in a list.
The practical rule: use <em> and <strong> almost always. If you only want
text to be bold for looks, that is CSS — font-weight: 700 on a class — not an
element.
And never <b> on a heading to make it bolder. It is already a heading.
The structural elements
This is the set that turns a soup of divs into a document. Each creates a landmark a screen reader user can jump to directly.
<body>
<header>
<h1>Sharma Kirana</h1>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/prices.html">Prices</a></li>
</ul>
</nav>
</header>
<main>
<article>
<h2>Now delivering in Kothrud</h2>
<p>From this week we deliver free above ₹500.</p>
</article>
<section>
<h2>Opening hours</h2>
<p>7am to 10pm, every day.</p>
</section>
</main>
<aside>
<h2>Find us</h2>
<p>12, Karve Road.</p>
</aside>
<footer>
<p>© 2026 Sharma Kirana</p>
</footer>
</body>
| Element | Use it for | Landmark |
|---|---|---|
<header> |
Introductory content for the page or a section | banner (at page level) |
<nav> |
A block of navigation links | navigation |
<main> |
The main content. Exactly one, not nested in the others | main |
<article> |
Something that makes sense on its own: a post, a product, a review | article |
<section> |
A thematic group, which should have a heading | region (if labelled) |
<aside> |
Related but tangential: a sidebar, a pull quote | complementary |
<footer> |
Closing content for the page or a section | contentinfo |
Three things worth knowing beyond the table:
<main> is the one to get right if you get only one right. Browsers and
screen readers offer "skip to main content" from it, which saves a keyboard user
tabbing through your entire navigation on every single page.
<section> without a heading is just a <div> with extra syllables. If you
cannot name it, use a <div>.
<header> and <footer> can appear more than once — one per <article> is
normal. Only the page-level ones are landmarks.
When <div> and <span> are correct
They are not forbidden. They are the right answer when there is no meaning to express and you need a box to style or a bit of text to hook onto:
<div class="card"> <!-- a grouping that exists only for layout -->
<h3>Atta</h3>
<p>₹<span class="price">450</span> per 10kg</p>
</div>
<div> is block-level and means nothing. <span> is inline and means nothing.
That is exactly their job. Reach for a semantic element first; fall back to
these when none fits.
The other useful ones
<blockquote cite="https://example.com">
<p>Best atta in Kothrud.</p>
<footer>— <cite>Pune Mirror</cite></footer>
</blockquote>
<p>Press <kbd>Ctrl + S</kbd> to save.</p>
<p>The file is <code>index.html</code>.</p>
<pre><code><h1>Hello</h1></code></pre>
<p>Open <time datetime="2026-10-02">2 October</time>.</p>
<p>Price is <s>₹500</s> ₹450.</p>
<p><abbr title="Maximum Retail Price">MRP</abbr> ₹500.</p>
<hr>
<time datetime="…"> is the one that pays off: a machine can read the date while
a human reads "2 October". <pre> preserves whitespace, which is why code blocks
use it — and inside it you must escape < as <, or the browser renders your
example instead of showing it.
The test that settles arguments
Load the page with CSS switched off. In devtools, Elements → find the
<link rel="stylesheet"> and delete it, or use a reader mode.
If the unstyled page still reads as a sensible document — a title, headings in order, paragraphs, a list of links — the HTML is good, and CSS is doing decoration rather than carrying meaning. If it reads as a meaningless stack of text, the markup is wrong no matter how good the styled version looks.
That is the whole lesson in one action, and it takes ten seconds.
Check your work
What semantic HTML means. Choosing elements for what the content means, not how it looks.
Why a div-only page fails. No heading list to navigate, no real links, no meaning for reader modes or search engines.
Heading rules. One h1, never skip a level down, and choose the level for
structure — size is CSS's job.
Why <br><br> is wrong for paragraphs. It makes two paragraphs into one, and
there is nothing to style.
What <br> is for. A break that is part of the content, like an address.
<em>/<strong> versus <i>/<b>. The first pair carry meaning a screen
reader conveys; the second set text apart without importance.
The one landmark to get right. <main>, because it powers "skip to main
content".
When <section> is pointless. When it has no heading — then it is a <div>.
When <div> and <span> are correct. When there is no meaning to express.
What <time datetime> buys. A machine-readable date beside the human one.
The ten-second test. Switch CSS off and see if it still reads as a document.
Practice
- Take the div-only example at the top and rewrite it with semantic elements.
- Build a page with
h1, twoh2s and anh3. Then view the heading tree in devtools' Accessibility tab. - Skip a level deliberately —
h1toh3— and look at the tree again. - Make an
h2smaller than itsh3with CSS. Confirm the outline is unaffected. - Write two paragraphs with
<br><br>, then correctly. Try to put 40px between them in both versions. - Mark up a shop address with
<br>correctly. - Wrap a warning in
<strong>and a stressed word in<em>. Listen to both with a screen reader if you can. - Build the full landmark skeleton — header, nav, main, aside, footer — and list the landmarks in devtools' Accessibility tree.
- Put a
<section>with no heading on the page. Justify it or change it to a<div>. - Add a
<time>, an<abbr>and a<blockquote>with a<cite>. - Delete your stylesheet link in devtools and read the page. Fix anything that stopped making sense.
- Find any real business website, delete its stylesheet in devtools, and judge its markup. Most fail.
Official documentation
- MDN — HTML elements reference — Every element, grouped by purpose. The page to browse when unsure.
- WHATWG HTML — Sections — The specification's own definitions of
article,section,navandaside, which settle most arguments. - W3C WAI — Page structure tutorial — Headings and landmarks explained from the assistive-technology side.
Next: links and images, and the attribute everybody leaves empty.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship