Text Semantics and Content Model
The Difference That Gets You Hired
In a frontend interview, you're asked to build a pricing card. Candidate A writes:
<div class="title">Pro Plan</div>
<div class="price">$29/mo</div>
<div class="features">
<div>Unlimited projects</div>
<div>Priority support</div>
</div>
Candidate B writes:
<article>
<h3>Pro Plan</h3>
<p><strong>$29</strong>/mo</p>
<ul>
<li>Unlimited projects</li>
<li>Priority support</li>
</ul>
</article>
Both look identical on screen. But Candidate B's version works with screen readers, gets indexed by search engines, and communicates meaning to any machine or human reading the source. Candidate A's is a pile of meaningless boxes.
Semantic HTML isn't about being pedantic. It's about writing markup that means something.
Think of HTML semantics like formatting a legal document vs. scribbling on a napkin. The napkin might have the same words, but a legal document has numbered sections, defined terms, clear hierarchies, and signatures — structure that gives the content its force. Semantic HTML gives your content its force on the web. Search engines, screen readers, browser reader modes, and AI tools all rely on that structure to understand what your page actually says.
Headings: Your Document's Outline
Headings create a hierarchical outline of your content. There are six levels, h1 through h6:
<h1>Frontend Engineering</h1>
<h2>HTML Foundations</h2>
<h3>Text Semantics</h3>
<h3>Document Structure</h3>
<h2>CSS Foundations</h2>
<h3>The Box Model</h3>
The rules:
- One
h1per page — it's the page's main topic. Think of it as the book title. - Don't skip levels — go h1 → h2 → h3. Never jump from h1 to h3 (screen readers announce the heading level, and a skip signals missing content).
- Headings are for hierarchy, not sizing — if you want bigger text, use CSS. Never pick an
h3because it "looks the right size."
Screen readers let users navigate by headings. If your heading hierarchy is broken, it's like a book where chapter 3 comes after chapter 1.
Text-Level Semantics
Emphasis vs. Decoration
This is where most developers get confused:
<!-- Semantic emphasis — screen readers stress this word -->
<p>You must <em>never</em> push to main without tests.</p>
<!-- Stronger emphasis / importance -->
<p><strong>Warning:</strong> This action cannot be undone.</p>
<!-- Visual-only bold/italic — no semantic meaning -->
<p>The movie <i>Inception</i> was released in 2010.</p>
<p>The keyword <b>class</b> is reserved in JavaScript.</p>
em— stress emphasis. The meaning of the sentence changes depending on which word is emphasized.strong— strong importance. "This part really matters."i— text in an alternate voice. Titles of works, technical terms, foreign words.b— draw attention without added importance. Keywords, product names.
The difference matters for accessibility. Screen readers change their intonation for em and strong. They don't change anything for i and b.
Other Important Text Elements
<!-- Abbreviation with expansion -->
<p>The <abbr title="World Wide Web Consortium">W3C</abbr> maintains web standards.</p>
<!-- Inline code -->
<p>Use <code>document.querySelector()</code> to select elements.</p>
<!-- Marked/highlighted text -->
<p>Search results: the word <mark>closure</mark> was found 3 times.</p>
<!-- Time (machine-readable) -->
<p>Published on <time datetime="2024-01-15">January 15, 2024</time>.</p>
<!-- Small print (side comments, legal text) -->
<p><small>Terms and conditions apply.</small></p>
<!-- Deleted and inserted text -->
<p>Price: <del>$49</del> <ins>$29</ins></p>
Each of these carries meaning that plain text doesn't. A time element with a datetime attribute lets search engines and calendar apps parse the date. An abbr with a title shows the full form on hover.
Lists: Ordered, Unordered, and Description
<!-- Unordered list — items have no specific sequence -->
<ul>
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
</ul>
<!-- Ordered list — sequence matters -->
<ol>
<li>Open the terminal</li>
<li>Run npm install</li>
<li>Start the dev server</li>
</ol>
<!-- Description list — key-value pairs -->
<dl>
<dt>HTML</dt>
<dd>A markup language for structuring web content.</dd>
<dt>CSS</dt>
<dd>A stylesheet language for visual presentation.</dd>
</dl>
Lists aren't just bullets and numbers. Screen readers announce "list, 3 items" when they encounter a ul, helping users understand the structure. Navigation menus are typically ul elements because they're lists of links.
Lists can be nested:
<ul>
<li>Frontend
<ul>
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
</ul>
</li>
<li>Backend
<ul>
<li>Node.js</li>
<li>Python</li>
</ul>
</li>
</ul>
The Content Model: What Goes Where
HTML elements are categorized by their content model — rules about what they can contain and where they can appear.
The main categories:
| Category | Description | Examples |
|---|---|---|
| Flow | Most elements — the general category | div, p, ul, h1-h6, article |
| Phrasing | Inline-level content | span, a, strong, em, code, img |
| Sectioning | Define document sections | article, section, nav, aside |
| Heading | Section headings | h1, h2, h3, h4, h5, h6 |
| Interactive | User-interactive elements | a, button, input, select, textarea |
| Embedded | External content | img, video, audio, iframe, canvas |
The key rules:
- Phrasing elements can only contain other phrasing elements — a
spancan hold astrong, but not adiv. - Some elements have specific content models —
ulcan only containlichildren.tablehas strict child requirements. - Interactive elements cannot nest — you can't put a
buttoninside ana, or anainside abutton.
<!-- Invalid: interactive inside interactive -->
<a href="/page">
<button>Click me</button>
</a>
<!-- Valid: use one or the other -->
<a href="/page">Click me</a>
<!-- or -->
<button onclick="navigate('/page')">Click me</button>
Production Scenario: Screen Reader Navigation
A screen reader user visits your page. Here's what they hear depending on your markup:
Bad markup (div soup):
<div class="heading">Features</div>
<div class="list">
<div>Fast</div>
<div>Reliable</div>
</div>
Screen reader announces: "Features. Fast. Reliable." — no structure, no navigation landmarks, no way to skip around.
Good markup (semantic):
<h2>Features</h2>
<ul>
<li>Fast</li>
<li>Reliable</li>
</ul>
Screen reader announces: "Heading level 2, Features. List, 2 items. Fast, 1 of 2. Reliable, 2 of 2." — the user knows exactly what they're dealing with and can skip the list if they want.
| What developers do | What they should do |
|---|---|
| Using h1-h6 to control text size instead of representing hierarchy Screen readers navigate by heading levels. Using h3 because it 'looks right' breaks the document outline | Use headings for document structure, CSS for sizing |
| Using br tags to create spacing between elements br is for line breaks in content, not for layout. A screen reader reads br as... nothing. CSS handles spacing | Use CSS margin or padding for spacing, br only for line breaks within text (like addresses or poems) |
| Wrapping everything in div and span elements div and span carry zero semantic meaning. They should be your last resort, not your first choice | Choose the most specific semantic element: article, section, nav, ul, p, time, code |
| Nesting interactive elements like a button inside an anchor The spec forbids interactive content inside interactive content. The click behavior is ambiguous and assistive tech can't resolve it | Use one interactive element — either a link or a button, not both |
Challenge: Rewrite Div Soup as Semantic HTML
Convert this markup to use proper semantic elements:
<div class="post">
<div class="post-title">Understanding Closures</div>
<div class="post-date">January 15, 2024</div>
<div class="post-body">
<div class="intro">Closures are one of JavaScript's most powerful features.</div>
<div class="section-title">What Is a Closure?</div>
<div class="text">A closure is a function that remembers its outer scope.</div>
<div class="key-points">
<div>Functions carry their scope</div>
<div>Variables stay alive through references</div>
<div>Memory cost depends on what's captured</div>
</div>
</div>
</div>
Show Answer
<article>
<h2>Understanding Closures</h2>
<p><time datetime="2024-01-15">January 15, 2024</time></p>
<p>Closures are one of JavaScript's most powerful features.</p>
<h3>What Is a Closure?</h3>
<p>A closure is a function that remembers its outer scope.</p>
<ul>
<li>Functions carry their scope</li>
<li>Variables stay alive through references</li>
<li>Memory cost depends on what's captured</li>
</ul>
</article>Changes made:
- Outer
div.post→article(self-contained content) div.post-title→h2(heading)div.post-date→timeinsidep(machine-readable date)div.introanddiv.text→p(paragraphs)div.section-title→h3(subheading under h2)div.key-pointswith child divs →ulwithliitems (unordered list)- Every element now carries semantic meaning for search engines, screen readers, and browser reader modes.
- 1Use headings for document hierarchy (one h1 per page, never skip levels) — never for visual sizing
- 2em means stress emphasis (changes sentence meaning), strong means importance — i and b are for alternate voice and attention without emphasis
- 3Choose the most specific semantic element available before falling back to div or span
- 4Interactive elements cannot nest inside other interactive elements — pick one and stick with it
- 5Content model rules determine what can nest where — phrasing elements cannot contain flow elements