HTML Foundations: The Complete Beginner's Guide to Building Web Page Structure
Every website you've ever visited — from Google to Netflix — starts with HTML. Not JavaScript. Not React. Not AI-generated code. Just plain, structured HTML.
The problem? Most beginners rush through HTML in a day, eager to "make things pretty" with CSS. Then they hit a wall: broken layouts, inaccessible forms, and CSS selectors that feel like they're fighting the markup instead of styling it.
This tutorial fixes that. We're going deep into HTML5 — the actual skeleton of the web — with real code you can copy, save as .html files, and open in your browser. No frameworks. No build tools. Just you and the browser.
By the end, you'll build a multi-page personal blog with clean structure, proper semantics, and a working contact form.
What You'll Build
A 3-page personal blog with:
- Homepage (
index.html) — blog post previews - About page (
about.html) — author bio - Contact page (
contact.html) — working form
No CSS yet. We're focusing purely on structure. Trust the process.
Part 1: The HTML Document Blueprint
Every HTML file follows the same blueprint. Understanding this upfront saves you hours of confusion later.
The Basic Structure
1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>My Personal Blog</title> 7</head> 8<body> 9 <!-- All visible content goes here --> 10</body> 11</html>
Let's Break It Down
<!DOCTYPE html> — This isn't actually an HTML tag. It's a declaration that tells the browser: "Hey, this is an HTML5 document. Render it in standards mode." Without it, browsers fall into "quirks mode" and render your page unpredictably (think: broken box models, weird spacing).
<html lang="en"> — The root element. The lang="en" attribute tells screen readers and search engines that the primary language is English. This matters for pronunciation, translation tools, and SEO.
<head> — The invisible brain of your page. Nothing inside <head> appears on the page, but it controls how the page behaves:
charset="UTF-8"— Ensures special characters (emojis, accented letters, currency symbols) render correctlyviewportmeta tag — Makes your page responsive on mobile devices. Without it, mobile browsers scale your desktop site down to a tiny unreadable size<title>— The text that appears in the browser tab and search engine results
<body> — Everything visible lives here. Text, images, links, forms, videos — all of it.
Pro Tip: Save this as
template.htmland copy it for every new page you build. You'll use this exact structure hundreds of times.
Part 2: Semantic HTML5 — Stop Using Div for Everything
HTML5 introduced semantic elements — tags that describe what the content is, not just how it looks. This helps screen readers, search engines, and your future self understand your code.
The Semantic Page Layout
1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>My Personal Blog</title> 7</head> 8<body> 9 10 <header> 11 <h1>My Personal Blog</h1> 12 <nav> 13 <ul> 14 <li><a href="index.html">Home</a></li> 15 <li><a href="about.html">About</a></li> 16 <li><a href="contact.html">Contact</a></li> 17 </ul> 18 </nav> 19 </header> 20 21 <main> 22 <!-- Main content goes here --> 23 </main> 24 25 <footer> 26 <p>© 2026 My Personal Blog. All rights reserved.</p> 27 </footer> 28 29</body> 30</html>
What Each Element Actually Means
| Element | Purpose | When to Use It |
|---|---|---|
<header> | Introductory content or navigation | Page banner, logo + nav combo |
<nav> | Major navigation links | Primary menu, table of contents |
<main> | The dominant content of the page | Use once per page. The reason someone visited |
<article> | Self-contained, independently distributable content | Blog posts, news articles, forum posts |
<section> | Thematic grouping of content | Chapters, tabbed content panels, distinct page regions |
<aside> | Tangentially related content | Sidebars, pull quotes, related links |
<footer> | Footer for its nearest sectioning content | Page footer, article footer with metadata |
When Is <div> Okay?
<div> is a generic container with zero semantic meaning. Use it when:
- You need a wrapper purely for styling (we'll add CSS later)
- No semantic element fits the content's purpose
- You're grouping elements for layout purposes
Bad:
1<div class="header"> 2 <div class="nav"> 3 <div class="link">Home</div> 4 </div> 5</div>
Good:
1<header> 2 <nav> 3 <a href="index.html">Home</a> 4 </nav> 5</header>
Why This Matters: Screen readers can jump between
<nav>,<main>, and<article>elements. They can't do that with<div class="nav">. And Google uses semantic structure to understand your content hierarchy for search rankings.
Part 3: Text Hierarchy — Headings, Paragraphs, and Lists
Browsers come with default styles, but the structure itself carries meaning. A heading isn't just "big bold text" — it's a navigational landmark.
Headings: The Outline of Your Page
1<h1>The Main Title of the Page</h1> 2<p>Introductory paragraph about what this page covers.</p> 3 4<h2>First Major Section</h2> 5<p>Content for the first section...</p> 6 7<h3>A Subsection Within the First Section</h3> 8<p>More detailed content...</p> 9 10<h2>Second Major Section</h2> 11<p>Content for the second section...</p>
The Heading Hierarchy Rule
Think of headings like a book outline:
<h1>— The book title. Use exactly once per page. It's what search engines use as the page title in results.<h2>— Chapter titles<h3>— Section titles within a chapter<h4>–<h6>— Subsections (rarely needed)
Never skip levels. Don't jump from <h2> to <h4> because you want smaller text. Use CSS for sizing. The heading level communicates structure, not style.
Accessibility Impact: Screen reader users navigate entire pages by jumping between headings. A broken hierarchy (
h1→h3→h2) is like a book with chapters in random order.
Paragraphs and Inline Text
1<p>This is a paragraph. Browsers add default spacing above and below.</p> 2 3<p>You can make text <strong>bold for importance</strong> or 4<em>italic for emphasis</em>. Don't use <b> or <i> — they carry no semantic meaning.</p> 5 6<p>Need a line break inside a paragraph?<br>Use the br tag sparingly — it's for poetry or addresses, not for creating space between paragraphs.</p> 7 8<p>The <abbr title="HyperText Markup Language">HTML</abbr> abbreviation tag adds helpful tooltips.</p> 9 10<p><cite>The Pragmatic Programmer</cite> is a classic dev book. The cite tag marks titles of creative work.</p>
Lists: Ordered, Unordered, and Definition
1<!-- Unordered list: items with no specific sequence --> 2<ul> 3 <li>HTML5 semantic elements</li> 4 <li>CSS Grid and Flexbox</li> 5 <li>JavaScript ES6+ features</li> 6</ul> 7 8<!-- Ordered list: items in a specific sequence --> 9<ol> 10 <li>Learn HTML structure</li> 11 <li>Master CSS styling</li> 12 <li>Build JavaScript interactivity</li> 13</ol> 14 15<!-- Definition list: term-description pairs --> 16<dl> 17 <dt>HTML</dt> 18 <dd>HyperText Markup Language — the standard markup for web pages</dd> 19 20 <dt>CSS</dt> 21 <dd>Cascading Style Sheets — controls presentation and layout</dd> 22</dl>
Real-World Use: Navigation menus are unordered lists. Recipe instructions are ordered lists. FAQ sections often use definition lists.
Part 4: Links and Navigation — The Web Is Built on Hyperlinks
Links are what make the web the web. Without <a> tags, you'd have 4.5 billion isolated documents.
Link Types You'll Use Daily
1<!-- Internal link: same folder --> 2<a href="about.html">About Me</a> 3 4<!-- Internal link: different folder --> 5<a href="blog/post-1.html">Read Post 1</a> 6 7<!-- External link: opens in new tab (use sparingly) --> 8<a href="https://developer.mozilla.org" target="_blank" rel="noopener noreferrer"> 9 MDN Web Docs 10</a> 11 12<!-- Email link --> 13<a href="mailto:hello@example.com">Send me an email</a> 14 15<!-- Phone link (taps to call on mobile) --> 16<a href="tel:+1234567890">Call us</a> 17 18<!-- Download link --> 19<a href="resume.pdf" download>Download my resume</a> 20 21<!-- Link to a specific section on the same page --> 22<a href="#contact-form">Jump to Contact Form</a> 23 24<!-- The target section --> 25<section id="contact-form"> 26 <h2>Contact Form</h2> 27 <!-- form content --> 28</section>
Why rel="noopener noreferrer" Matters
When you use target="_blank", the new page gets access to your page via window.opener. Malicious sites can redirect your original tab to a phishing page. The rel attribute prevents this.
Always add
rel="noopener noreferrer"to externaltarget="_blank"links. No exceptions.
Skip-to-Content Link (Accessibility Essential)
Keyboard users and screen reader users don't want to tab through 20 navigation links on every page. A skip link lets them jump straight to the main content.
1<body> 2 <!-- Hidden by default, visible on focus --> 3 <a href="#main-content" class="skip-link">Skip to main content</a> 4 5 <header> 6 <nav> 7 <!-- 15 navigation links --> 8 </nav> 9 </header> 10 11 <main id="main-content"> 12 <!-- Content starts here --> 13 </main> 14</body>
1/* Add this to your CSS later */ 2.skip-link { 3 position: absolute; 4 top: -40px; 5 left: 0; 6 background: #000; 7 color: #fff; 8 padding: 8px; 9 z-index: 100; 10} 11 12.skip-link:focus { 13 top: 0; 14}
This is required by WCAG accessibility standards. Major websites (BBC, Gov.uk, every government site) use skip links. It's a small addition that makes your site usable for millions of people.
Part 5: Images and Media — Beyond Just Plopping Files on a Page
Images are the heaviest assets on most web pages. How you add them affects performance, accessibility, and SEO.
The Modern Image Tag
1<!-- Basic image --> 2<img src="photo.jpg" alt="A golden retriever playing in a park"> 3 4<!-- Responsive image with multiple sources --> 5<picture> 6 <source srcset="hero-large.avif" type="image/avif" media="(min-width: 1200px)"> 7 <source srcset="hero-large.webp" type="image/webp" media="(min-width: 1200px)"> 8 <source srcset="hero-small.avif" type="image/avif"> 9 <img src="hero-fallback.jpg" alt="Mountain landscape at sunset" loading="lazy"> 10</picture> 11 12<!-- Image with dimensions (prevents layout shift) --> 13<img src="avatar.jpg" 14 alt="Profile photo of Jane Doe" 15 width="200" 16 height="200" 17 loading="lazy">
The alt Attribute: Non-Negotiable
The alt text serves three purposes:
- Screen readers read it aloud to blind users
- Search engines use it to understand image content
- Broken images display the alt text instead of a broken icon
Good alt text:
1<img src="chart-sales-2026.png" alt="Bar chart showing Q1-Q4 sales growth, with Q4 reaching $2.4M">
Bad alt text:
1<img src="chart-sales-2026.png" alt="chart"> <!-- Too vague --> 2<img src="chart-sales-2026.png" alt=""> <!-- Only if image is purely decorative -->
Rule of thumb: If you were reading your page over the phone to a friend, how would you describe the image? That's your alt text.
Figures and Captions
1<figure> 2 <img src="code-screenshot.png" alt="VS Code editor showing HTML syntax highlighting"> 3 <figcaption> 4 Figure 1: VS Code with the Live Server extension running on port 5500. 5 </figcaption> 6</figure>
<figure> groups related media. <figcaption> adds a visible caption. Screen readers announce the relationship automatically.
Video and Audio
1<!-- Video with multiple formats and fallback --> 2<video controls width="640" height="360" poster="thumbnail.jpg"> 3 <source src="tutorial.mp4" type="video/mp4"> 4 <source src="tutorial.webm" type="video/webm"> 5 <p>Your browser doesn't support HTML5 video. 6 <a href="tutorial.mp4">Download the video</a> instead.</p> 7</video> 8 9<!-- Audio --> 10<audio controls> 11 <source src="podcast.mp3" type="audio/mpeg"> 12 <source src="podcast.ogg" type="audio/ogg"> 13 <p>Your browser doesn't support HTML5 audio.</p> 14</audio>
controlsadds play/pause buttonspostershows a thumbnail before the video plays- Multiple
<source>tags let the browser pick the format it supports - The text inside the tag is fallback content for very old browsers
Embedding External Content
1<!-- YouTube embed (use lazy loading) --> 2<iframe 3 src="https://www.youtube.com/embed/dQw4w9WgXcQ" 4 title="Web Development Tutorial Video" 5 width="560" 6 height="315" 7 loading="lazy" 8 allowfullscreen> 9</iframe>
Always add a title to iframes — screen readers announce it as the frame's name.
Part 6: Tables — For Data, Not Layout
In 2010, people used <table> for entire page layouts. Don't do that. Tables are for tabular data. Period.
A Proper Data Table
1<table> 2 <caption>Monthly Blog Traffic 2026</caption> 3 4 <thead> 5 <tr> 6 <th scope="col">Month</th> 7 <th scope="col">Visitors</th> 8 <th scope="col">Page Views</th> 9 <th scope="col">Bounce Rate</th> 10 </tr> 11 </thead> 12 13 <tbody> 14 <tr> 15 <th scope="row">January</th> 16 <td>1,240</td> 17 <td>3,720</td> 18 <td>45%</td> 19 </tr> 20 <tr> 21 <th scope="row">February</th> 22 <td>1,580</td> 23 <td>4,740</td> 24 <td>42%</td> 25 </tr> 26 <tr> 27 <th scope="row">March</th> 28 <td>2,100</td> 29 <td>6,300</td> 30 <td>38%</td> 31 </tr> 32 </tbody> 33 34 <tfoot> 35 <tr> 36 <th scope="row">Total</th> 37 <td>4,920</td> 38 <td>14,760</td> 39 <td>—</td> 40 </tr> 41 </tfoot> 42</table>
Table Elements Explained
| Element | Purpose |
|---|---|
<caption> | Table title (announced by screen readers before the data) |
<thead> | Header rows — repeated on printed pages |
<tbody> | Main data rows |
<tfoot> | Footer rows — totals, summaries |
<tr> | Table row |
<th> | Header cell (use scope="col" or scope="row" for screen readers) |
<td> | Standard data cell |
colspan | Makes a cell span multiple columns |
rowspan | Makes a cell span multiple rows |
The
scopeattribute tells screen readers whether a header applies to a column or a row. Without it, a screen reader user hears "1,240" with no context. With it, they hear "January, Visitors, 1,240."
Part 7: HTML Forms — Where User Interaction Begins
Forms are how users search, log in, sign up, comment, and buy things. A broken form is a broken business.
The Complete Contact Form
1<form action="/submit-contact" method="POST"> 2 3 <!-- Text input --> 4 <div> 5 <label for="full-name">Full Name</label> 6 <input 7 type="text" 8 id="full-name" 9 name="full_name" 10 placeholder="Jane Doe" 11 required 12 minlength="2" 13 maxlength="100"> 14 </div> 15 16 <!-- Email input --> 17 <div> 18 <label for="email">Email Address</label> 19 <input 20 type="email" 21 id="email" 22 name="email" 23 placeholder="jane@example.com" 24 required> 25 </div> 26 27 <!-- Telephone --> 28 <div> 29 <label for="phone">Phone Number</label> 30 <input 31 type="tel" 32 id="phone" 33 name="phone" 34 pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" 35 placeholder="123-456-7890"> 36 <small>Format: 123-456-7890</small> 37 </div> 38 39 <!-- Dropdown select --> 40 <div> 41 <label for="topic">Topic</label> 42 <select id="topic" name="topic" required> 43 <option value="" disabled selected>Select a topic</option> 44 <option value="general">General Inquiry</option> 45 <option value="collaboration">Collaboration</option> 46 <option value="feedback">Feedback</option> 47 <option value="bug">Report a Bug</option> 48 </select> 49 </div> 50 51 <!-- Textarea --> 52 <div> 53 <label for="message">Your Message</label> 54 <textarea 55 id="message" 56 name="message" 57 rows="6" 58 cols="50" 59 placeholder="Tell me what's on your mind..." 60 required 61 minlength="10"></textarea> 62 </div> 63 64 <!-- Radio buttons --> 65 <fieldset> 66 <legend>Preferred Contact Method</legend> 67 68 <input type="radio" id="contact-email" name="contact_method" value="email" checked> 69 <label for="contact-email">Email</label> 70 71 <input type="radio" id="contact-phone" name="contact_method" value="phone"> 72 <label for="contact-phone">Phone</label> 73 </fieldset> 74 75 <!-- Checkbox --> 76 <div> 77 <input type="checkbox" id="newsletter" name="newsletter" value="yes"> 78 <label for="newsletter">Subscribe to my monthly newsletter</label> 79 </div> 80 81 <!-- Submit button --> 82 <button type="submit">Send Message</button> 83 84 <!-- Reset button (use sparingly) --> 85 <button type="reset">Clear Form</button> 86 87</form>
Critical Form Concepts
for and id pairing:
The <label for="email"> must match the <input id="email">. This creates a clickable label — clicking the text focuses the input. It also tells screen readers which label belongs to which field.
name attribute:
This is what the server receives when the form is submitted. Without name, the data doesn't get sent.
type matters:
type="email"— shows@shortcut on mobile keyboards, validates email formattype="tel"— shows numeric keypad on mobiletype="url"— validates URL format, shows.comshortcuttype="number"— shows numeric keypad, prevents letter inputtype="date"— shows date picker on supported browsers
Validation attributes:
required— field must be filledminlength/maxlength— character limitspattern— regex validation (e.g., phone format)min/max— for numbers and dates
GET vs POST:
- GET — Appends data to the URL (
?name=John&email=john@example.com). Use for searches, filters, anything non-sensitive. Limited to ~2048 characters. - POST — Sends data in the request body. Use for passwords, personal info, large data. Required for file uploads.
Security Note: Client-side validation (HTML attributes) is for user experience. Server-side validation is for security. Never trust form data that hasn't been validated on the server.
Grouping Related Fields
1<fieldset> 2 <legend>Shipping Address</legend> 3 4 <label for="street">Street Address</label> 5 <input type="text" id="street" name="street" required> 6 7 <label for="city">City</label> 8 <input type="text" id="city" name="city" required> 9 10 <label for="zip">ZIP Code</label> 11 <input type="text" id="zip" name="zip" pattern="[0-9]{5}" required> 12</fieldset>
<fieldset> groups related controls. <legend> provides the group title. Screen readers announce: "Shipping Address, grouping."
Part 8: Putting It All Together — The Complete Blog
Now let's build the actual project. Create a folder called my-blog with these three files.
File 1: index.html (Homepage)
1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>My Personal Blog</title> 7</head> 8<body> 9 10 <a href="#main-content" class="skip-link">Skip to main content</a> 11 12 <header> 13 <h1>My Personal Blog</h1> 14 <p>Thoughts on web development, design, and technology</p> 15 16 <nav> 17 <ul> 18 <li><a href="index.html" aria-current="page">Home</a></li> 19 <li><a href="about.html">About</a></li> 20 <li><a href="contact.html">Contact</a></li> 21 </ul> 22 </nav> 23 </header> 24 25 <main id="main-content"> 26 <section> 27 <h2>Latest Posts</h2> 28 29 <article> 30 <header> 31 <h3><a href="posts/semantic-html.html">Why Semantic HTML Matters More Than Ever</a></h3> 32 <p> 33 <time datetime="2026-08-10">August 10, 2026</time> 34 | <span>8 min read</span> 35 </p> 36 </header> 37 <p>Semantic HTML isn't just about SEO. It's about building a web that works for everyone — including the 1 billion people worldwide with disabilities...</p> 38 <a href="posts/semantic-html.html">Read full article →</a> 39 </article> 40 41 <article> 42 <header> 43 <h3><a href="posts/css-grid-guide.html">CSS Grid: A Visual Guide for Beginners</a></h3> 44 <p> 45 <time datetime="2026-08-05">August 5, 2026</time> 46 | <span>12 min read</span> 47 </p> 48 </header> 49 <p>Stop fighting with floats. CSS Grid is the layout system the web has been waiting for since 1996. Here's everything you need to know...</p> 50 <a href="posts/css-grid-guide.html">Read full article →</a> 51 </article> 52 53 <article> 54 <header> 55 <h3><a href="posts/web-accessibility.html">Web Accessibility: Not Optional</a></h3> 56 <p> 57 <time datetime="2026-07-28">July 28, 2026</time> 58 | <span>6 min read</span> 59 </p> 60 </header> 61 <p>Accessibility isn't a feature you bolt on at the end. It's a mindset that should guide every decision from the first line of HTML...</p> 62 <a href="posts/web-accessibility.html">Read full article →</a> 63 </article> 64 </section> 65 </main> 66 67 <footer> 68 <p>© 2026 My Personal Blog. Built with semantic HTML5.</p> 69 <p> 70 <a href="https://github.com/yourusername" target="_blank" rel="noopener noreferrer">GitHub</a> | 71 <a href="https://twitter.com/yourusername" target="_blank" rel="noopener noreferrer">Twitter</a> | 72 <a href="https://linkedin.com/in/yourusername" target="_blank" rel="noopener noreferrer">LinkedIn</a> 73 </p> 74 </footer> 75 76</body> 77</html>
File 2: about.html (About Page)
1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>About Me | My Personal Blog</title> 7</head> 8<body> 9 10 <a href="#main-content" class="skip-link">Skip to main content</a> 11 12 <header> 13 <h1>My Personal Blog</h1> 14 <nav> 15 <ul> 16 <li><a href="index.html">Home</a></li> 17 <li><a href="about.html" aria-current="page">About</a></li> 18 <li><a href="contact.html">Contact</a></li> 19 </ul> 20 </nav> 21 </header> 22 23 <main id="main-content"> 24 <article> 25 <header> 26 <h2>About Me</h2> 27 </header> 28 29 <img src="images/profile.jpg" alt="Alex Chen standing in front of a bookshelf filled with programming books" width="300" height="300"> 30 31 <p>Hi, I'm <strong>Alex Chen</strong>, a frontend developer based in San Francisco. I started coding in 2022 after leaving a career in finance, and I've been obsessed with the web ever since.</p> 32 33 <p>My focus is on <em>accessible, performant, and beautiful</em> web experiences. I believe that good code should work for everyone — regardless of device, connection speed, or ability.</p> 34 35 <h3>What I Write About</h3> 36 <ul> 37 <li>HTML & CSS best practices</li> 38 <li>Modern JavaScript and frameworks</li> 39 <li>Web accessibility (a11y)</li> 40 <li>Performance optimization</li> 41 <li>Career advice for self-taught developers</li> 42 </ul> 43 44 <h3>My Stack</h3> 45 <dl> 46 <dt>Frontend</dt> 47 <dd>HTML5, CSS3, JavaScript (ES6+), React, Vue</dd> 48 49 <dt>Tools</dt> 50 <dd>VS Code, Git, Figma, Vite</dd> 51 52 <dt>Currently Learning</dt> 53 <dd>Rust, WebAssembly, Three.js</dd> 54 </dl> 55 </article> 56 </main> 57 58 <footer> 59 <p>© 2026 My Personal Blog. All rights reserved.</p> 60 </footer> 61 62</body> 63</html>
File 3: contact.html (Contact Page)
1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Contact | My Personal Blog</title> 7</head> 8<body> 9 10 <a href="#main-content" class="skip-link">Skip to main content</a> 11 12 <header> 13 <h1>My Personal Blog</h1> 14 <nav> 15 <ul> 16 <li><a href="index.html">Home</a></li> 17 <li><a href="about.html">About</a></li> 18 <li><a href="contact.html" aria-current="page">Contact</a></li> 19 </ul> 20 </nav> 21 </header> 22 23 <main id="main-content"> 24 <section> 25 <h2>Get In Touch</h2> 26 <p>Have a question, collaboration idea, or just want to say hi? Fill out the form below or <a href="mailto:hello@alexchen.dev">email me directly</a>.</p> 27 28 <form action="/submit-contact" method="POST"> 29 30 <fieldset> 31 <legend>Your Information</legend> 32 33 <div> 34 <label for="full-name">Full Name *</label> 35 <input 36 type="text" 37 id="full-name" 38 name="full_name" 39 placeholder="Jane Doe" 40 required 41 minlength="2" 42 autocomplete="name"> 43 </div> 44 45 <div> 46 <label for="email">Email Address *</label> 47 <input 48 type="email" 49 id="email" 50 name="email" 51 placeholder="jane@example.com" 52 required 53 autocomplete="email"> 54 </div> 55 </fieldset> 56 57 <fieldset> 58 <legend>Message Details</legend> 59 60 <div> 61 <label for="topic">Topic *</label> 62 <select id="topic" name="topic" required> 63 <option value="" disabled selected>Select a topic</option> 64 <option value="general">General Inquiry</option> 65 <option value="collaboration">Collaboration</option> 66 <option value="feedback">Feedback</option> 67 <option value="bug">Report a Bug</option> 68 </select> 69 </div> 70 71 <div> 72 <label for="message">Your Message *</label> 73 <textarea 74 id="message" 75 name="message" 76 rows="8" 77 cols="50" 78 placeholder="Tell me what's on your mind..." 79 required 80 minlength="10"></textarea> 81 </div> 82 </fieldset> 83 84 <fieldset> 85 <legend>Preferences</legend> 86 87 <div> 88 <input type="checkbox" id="newsletter" name="newsletter" value="yes"> 89 <label for="newsletter">Subscribe to my monthly newsletter</label> 90 </div> 91 92 <div> 93 <input type="checkbox" id="privacy" name="privacy" value="accepted" required> 94 <label for="privacy">I agree to the <a href="privacy.html">privacy policy</a> *</label> 95 </div> 96 </fieldset> 97 98 <button type="submit">Send Message</button> 99 100 </form> 101 </section> 102 103 <aside> 104 <h3>Other Ways to Reach Me</h3> 105 <ul> 106 <li><a href="https://twitter.com/yourusername" target="_blank" rel="noopener noreferrer">Twitter / X</a></li> 107 <li><a href="https://linkedin.com/in/yourusername" target="_blank" rel="noopener noreferrer">LinkedIn</a></li> 108 <li><a href="https://github.com/yourusername" target="_blank" rel="noopener noreferrer">GitHub</a></li> 109 </ul> 110 111 <h3>Response Time</h3> 112 <p>I typically respond within 48 hours. For urgent matters, please mention it in the subject line.</p> 113 </aside> 114 </main> 115 116 <footer> 117 <p>© 2026 My Personal Blog. All rights reserved.</p> 118 </footer> 119 120</body> 121</html>
Part 9: Testing Your Work
Before you move on to CSS, verify your HTML is solid.
1. Validate Your Markup
Go to validator.w3.org and paste your HTML. Fix every error. A single unclosed tag can break your entire layout once CSS is applied.
2. Test Without CSS
Open your pages in the browser. They should be fully readable and navigable. If your content makes sense without any styling, you've written good HTML. This is the "naked test."
3. Keyboard Navigation Test
Press Tab to move through your page. Can you reach every link and form field? Does the focus order make sense? If not, check your HTML structure — not your CSS.
4. Screen Reader Test (Optional but Recommended)
If you're on Mac, press Cmd + F5 to turn on VoiceOver. Navigate by headings (Ctrl + Option + Cmd + H). Can you understand your page structure just by listening?
Common Beginner Mistakes to Avoid
| Mistake | Why It's Wrong | The Fix |
|---|---|---|
Using <br> for spacing | It's for line breaks in addresses/poetry, not layout | Use <p> tags or CSS margin |
Skipping alt attributes | Blind users have no context for images | Always add descriptive alt text |
Multiple <h1> tags | Breaks the document outline | One <h1> per page |
<div> soup | Zero semantic meaning | Use <header>, <nav>, <main>, <article>, <footer> |
Missing <label> associations | Screen readers can't identify form fields | Match for with id |
target="_blank" without rel | Security vulnerability | Add rel="noopener noreferrer" |
| Tables for layout | Breaks on mobile, terrible for screen readers | Use CSS Grid or Flexbox (coming in Module 2) |
What's Next?
You now have a fully structured, semantic, accessible 3-page blog. It looks plain — and that's exactly right. You built the skeleton before adding the skin.
Module 2 covers CSS Visual Design: colors, typography, the box model, and making your blog actually look like a professional website. Every selector you write will target the clean structure you just built.
Save your my-blog folder. We're coming back to it.
Quick Reference Cheat Sheet
1<!-- Page structure --> 2<header>, <nav>, <main>, <article>, <section>, <aside>, <footer> 3 4<!-- Text --> 5<h1> to <h6>, <p>, <strong>, <em>, <abbr>, <cite>, <blockquote> 6 7<!-- Lists --> 8<ul>, <ol>, <li>, <dl>, <dt>, <dd> 9 10<!-- Links --> 11<a href="...">, <a href="mailto:...">, <a href="tel:..."> 12 13<!-- Media --> 14<img src="..." alt="...">, <picture>, <video>, <audio>, <iframe> 15 16<!-- Tables --> 17<table>, <thead>, <tbody>, <tfoot>, <tr>, <th scope="col|row">, <td> 18 19<!-- Forms --> 20<form>, <label for="...">, <input type="...">, <textarea>, <select>, 21<button type="submit">, <fieldset>, <legend>
Found this helpful? Bookmark it. In Module 2, we'll style this exact blog and you'll see why clean HTML makes CSS a joy instead of a fight.