Skip to content
C Codeloom
HTML & CSS

HTML Semantic Elements and Accessibility in Practice

Choose the right HTML element for the job and your site becomes more accessible, more SEO-friendly, and easier to style. Practical patterns and pitfalls.

·5 min read · By Codeloom
Beginner 9 min read

What you'll learn

  • Why semantic HTML matters for users and machines
  • When to use header, main, nav, article, section
  • How landmarks help screen readers
  • How to label buttons, links, and form controls
  • Quick fixes that improve accessibility right now

Prerequisites

  • Basic HTML familiarity

What and Why

You can build almost anything with <div> and <span>. You should not. Semantic HTML uses elements whose names describe what the content means: a <button> is a button, a <nav> is navigation, an <h2> is a second-level heading. Browsers, search engines, and assistive technology rely on those meanings.

The payoff is real: keyboard users tab through your page in a logical order, screen readers announce regions and headings, search engines index structure, and your CSS selectors stay simple. Best of all, semantic HTML costs nothing extra to write.

Mental Model

Think of an HTML document as having three layers: structure (what is it), content (the words), and behavior/style (added later). Semantic elements live in the structure layer. They form a tree of landmarks and headings that an assistive tech can navigate.

<html>
<head> ... </head>
<body>
  <header>      site banner, logo, top nav
  <nav>         primary navigation
  <main>        the unique content of this page
    <article>   a self-contained piece
      <h1>      one main heading
      <section> grouped content with its own heading
    </article>
    <aside>     tangentially related content
  </main>
  <footer>      site info, links
</body>
</html>
A typical semantic page outline

Each region (header, nav, main, aside, footer) is a landmark. Screen readers offer a jump-to-landmark shortcut, so users can skip past navigation without listening to it on every page.

Hands-on Example

Replace this:

<div class="top">
  <div class="logo">Acme</div>
  <div class="links">
    <div onclick="go('/')">Home</div>
    <div onclick="go('/blog')">Blog</div>
  </div>
</div>

<div class="content">
  <div class="title">Welcome</div>
  <div class="post">
    <div class="post-title">Hello</div>
    <div>Lorem ipsum...</div>
  </div>
</div>

With this:

<header>
  <a href="/" class="logo">Acme</a>
  <nav aria-label="Primary">
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/blog">Blog</a></li>
    </ul>
  </nav>
</header>

<main>
  <h1>Welcome</h1>
  <article>
    <h2>Hello</h2>
    <p>Lorem ipsum...</p>
  </article>
</main>

Notice the navigation uses real <a> tags, not clickable divs. Real links are focusable, support middle-click to open in new tab, work without JavaScript, and are announced as links.

Forms benefit even more:

<form>
  <label for="email">Email</label>
  <input id="email" name="email" type="email" required />

  <fieldset>
    <legend>Plan</legend>
    <label><input type="radio" name="plan" value="free" /> Free</label>
    <label><input type="radio" name="plan" value="pro" /> Pro</label>
  </fieldset>

  <button type="submit">Sign up</button>
</form>

Every input has a connected <label>; clicking the label focuses the input, and screen readers announce the label when the user reaches the field. The <fieldset> plus <legend> groups the radio buttons with a heading.

Choose buttons vs links by behavior, not look. A button performs an action on the current page; a link navigates. Style them however you like, but pick the right element:

<button type="button" onclick="openMenu()">Menu</button>
<a href="/help">Help</a>

For images, every meaningful image needs alt text describing what it shows. Decorative images use alt="" so screen readers skip them.

Common Pitfalls

Skipping heading levels. Going from <h1> to <h3> confuses readers and accessibility trees. Use levels in order; you can style any level to look like any other.

Using clickable divs. They are not focusable, not keyboard operable, and not announced as interactive. If you need a button, use <button>.

Multiple <h1> per page. Modern HTML allows it inside <section> and <article>, but a single document-level <h1> is still the safest and most readable convention.

Hiding focus outlines globally. outline: none without a replacement makes keyboard navigation invisible. Style the focus state, do not erase it. :focus-visible lets you show focus only for keyboard users.

Relying on color alone. Red text for errors is fine; red text plus an icon plus a clear message is accessible. Tools like Lighthouse and axe catch many of these in seconds.

Practical Tips

Run your page with the keyboard only: Tab through, Shift+Tab back, Enter to activate. If you cannot reach or use something, neither can a keyboard-dependent user.

Use the browser’s accessibility tree inspector (in Chrome dev tools under Elements > Accessibility) to see how assistive tech reads your page. Names, roles, and landmarks should match your intent.

Prefer native elements over ARIA. <button> already has the right role, focus behavior, and keyboard handling; role="button" on a div makes you reimplement all of that, badly.

Use aria-label to name landmarks when you have more than one of the same type (“Primary navigation”, “Footer navigation”). Use aria-current="page" on the active nav link.

Add lang="en" (or whichever language) to your <html> tag. It improves screen reader pronunciation and helps translation tools.

Wrap-up

Semantic HTML is the cheapest accessibility win you can make. Use the right element for the job, give every form control a label, keep heading levels in order, and treat focus styles as a feature. Your users with assistive tech, your SEO, and your future self maintaining the CSS will all thank you.