How to Structure a Web Page With Semantic HTML
A beginner guide to building a page with meaningful HTML elements instead of using generic divs for every part.
By the end: You will have a valid page skeleton with header/nav/main/sections/footer, a logical heading structure and native links/buttons/forms.
You will have a valid page skeleton with header/nav/main/sections/footer, a logical heading structure and native links/buttons/forms.
Key terms before you start
These definitions make the steps easier to follow and help distinguish controls, files and concepts that can look similar at first.
What you need before starting
- A code editor
- A local/staging web page you are allowed to edit
- A modern browser with Developer Tools
Step-by-step tutorial
Every step is shown below. Work through them in order and use each checkpoint to confirm the result before moving on.
Work on a local or staging copy
Open the project containing the HTML/CSS source. If this is a production site, use a staging copy or backup before structural changes.
A browser page is rendered from source files/CMS output; DevTools edits do not save back to the server.
Expected: DevTools edits are temporary. Copy the tested change into the real source/CMS and save it.
Open the page in a modern browser
Load the page, then press F12 or Ctrl+Shift+I (Windows/Linux) to open Developer Tools.
DevTools lets you inspect the DOM, applied CSS, computed sizes, requests and console errors.
Inspect specificity/order/inheritance in DevTools before adding !important.
Create the document skeleton
Start with <!doctype html>, <html lang="en">, <head> and <body>.
The doctype selects modern HTML parsing; lang helps assistive technology choose language rules.
Add the site header
Inside <body>, add <header> for introductory/site-header content.
Header does not automatically mean “top of screen”; it identifies introductory content for its context.
Test realistic/long content and fix intrinsic sizing/wrapping rather than designing around short demo text.
Add primary navigation
Place major site links inside <nav aria-label="Primary">.
Use <a href="..."> for navigation because links have destination semantics and browser behavior.
Add one main landmark
Add a single <main> containing the page-specific content.
Main should not contain repeated site-wide header/footer content.
Test realistic/long content and fix intrinsic sizing/wrapping rather than designing around short demo text.
Write the page H1
Place an <h1> naming the page subject near the start of main.
Heading levels describe hierarchy, not font size.
Create meaningful sections
Use <section> for thematic groups and give each important section a heading.
A section with no thematic purpose may be better as a div/group.
Inspect specificity/order/inheritance in DevTools before adding !important.
Use paragraphs and lists according to content
Put prose in <p>; use <ul>/<ol> when content is actually a list/sequence.
Do not add <br> repeatedly to fake paragraph spacing.
Test realistic/long content and fix intrinsic sizing/wrapping rather than designing around short demo text.
Use button versus link correctly
Use <button> for an action on the current interface; use <a href> to navigate to another URL/location.
Styling can make them look similar without changing semantics.
Add the footer
Put repeated closing/site information in <footer>.
A footer can contain legal/navigation/contact information and is separate from main page content.
Test realistic/long content and fix intrinsic sizing/wrapping rather than designing around short demo text.
Disable CSS mentally/temporarily
In DevTools disable the stylesheet or inspect the DOM order.
Semantic structure should still read in a logical sequence.
Inspect specificity/order/inheritance in DevTools before adding !important.
Check heading outline
Use DevTools accessibility tools/heading inspection or read the h1/h2/h3 order.
Do not skip levels merely for appearance.
Inspect specificity/order/inheritance in DevTools before adding !important.
Test the public/staging URL logged out
Open the final URL in a private/incognito window.
CMS/admin sessions can hide caching, permission or styling differences.
Test realistic/long content and fix intrinsic sizing/wrapping rather than designing around short demo text.
Document the changed file/rule
Record the file/template/component and purpose of the change in the project notes or version-control commit.
Future maintainers should not have to reverse-engineer why a rule exists.
Expected: DevTools edits are temporary. Copy the tested change into the real source/CMS and save it.
Test the rendered page, not only the source code.
Concrete examples
Copyable example
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Roof repair services</title>
</head>
<body>
<header>
<nav aria-label="Primary">
<a href="/">Home</a>
<a href="/services/">Services</a>
<a href="/contact/">Contact</a>
</nav>
</header>
<main>
<h1>Roof repair services</h1>
<section aria-labelledby="repairs">
<h2 id="repairs">What we repair</h2>
<p>Replace this with factual service information.</p>
</section>
</main>
<footer>© Example business</footer>
</body>
</html>Troubleshooting
Expected: DevTools edits are temporary. Copy the tested change into the real source/CMS and save it.
Test realistic/long content and fix intrinsic sizing/wrapping rather than designing around short demo text.
Inspect specificity/order/inheritance in DevTools before adding !important.
The change has a traceable reason/location.
Continue with these tutorials
Documentation and references
Use these primary and supporting sources to verify current controls, browser behavior and product-specific details.