In Chapter 1 you saw the whole web loop: the browser asks, a server answers. The answer -- the thing the server sends back -- is almost always HTML. It's the first real code you'll write, and it's the scaffolding every page on the internet is built on.
By the end of this chapter you'll know what HTML actually is, how the browser turns it into a tree called the DOM, the roughly 20 elements you'll use 90% of the time, how to write semantic HTML (not just a sea of <div>s), how forms work, and five accessibility rules that make your pages usable by everyone -- including search engines.
What HTML Actually Is
HTML stands for HyperText Markup Language. Each word matters:
- HyperText -- text that links to other text. The
<a>("anchor") tag is what makes it hyper. - Markup -- you mark up plain text with tags that give it meaning.
<h1>Hello</h1>means "this text is the top-level heading." The tags wrap content to describe what it is. - Language -- there are rules for how tags nest and what they mean. Browsers enforce these rules.
Here's the key thing most tutorials skip: HTML is not a programming language. There are no variables, no loops, no if-statements. You can't compute anything. HTML only describes structure. It says "this is a heading, that's a paragraph, here's a link." Making things happen -- a button that adds to a cart, an input that validates -- is JavaScript's job. Making things look a certain way is CSS's job. HTML's only job is structure.
That sounds limiting, but it's the whole reason the web works. Because HTML is pure description, every browser on every device can render the same HTML differently to fit its screen -- a phone, a smart TV, a screen reader for a blind user, a search engine crawler. They all read the same markup and decide how to present it.
The DOM — Why HTML Is Actually a Tree
When the browser receives HTML, it doesn't just dump the text onto the screen. It parses the tags into a data structure called the Document Object Model, or DOM. The DOM is a tree: every element is a node, every nested element is a child of the one around it.
Consider this tiny page:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
</head>
<body>
<header>
<h1>Hello, world</h1>
</header>
<main>
<p>Welcome to <a href="/">my site</a>.</p>
</main>
</body>
</html>
The browser builds it into this tree:
Figure 1 — How the browser turns HTML into a DOM tree. Element nodes in blue, text nodes in grey, the root document in yellow.
Every web page on the internet is a tree like this. When you use JavaScript to read or change a page (document.querySelector('h1')), you're walking the tree. When CSS targets elements (header h1 { color: red }), it's matching patterns in the tree. When a screen reader announces a page, it's reading the tree in order.
Every Page Has the Same Skeleton
Every HTML document on the web has the same five-part skeleton. Memorise it now and you'll never have to look it up again.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Page</title>
</head>
<body>
<!-- everything the user sees goes here -->
</body>
</html>
Line by line:
| Line | What it does |
|---|---|
| <!DOCTYPE html> | "This is HTML5." Has to be the very first line. Without it, browsers switch to "quirks mode" and render weirdly. |
| <html lang="en"> | The root element. The lang attribute tells screen readers, search engines, and browser translators what language the content is in. |
| <head> | Metadata: things about the page that aren't shown in the page body. Title, character encoding, viewport settings, CSS links, script links, SEO tags. |
| <meta charset="UTF-8"> | "The text in this page is UTF-8 encoded." Put this first in <head> or emoji and accented characters will break. |
| <meta name="viewport"…> | Tells mobile browsers not to zoom out pretending they're a desktop. Without this, every iPhone visitor sees your site at 25% zoom. |
| <title> | Shown in the browser tab, bookmarks, search results. Write it for humans, not robots. |
| <body> | Everything the user actually sees. |
Copy that snippet. Save it as index.html. Double-click the file. It opens in your browser. You just shipped your first web page. Seriously -- that's what every web page starts as.
The Elements You'll Use 90% of the Time
HTML has about 140 tags. You will use maybe 25 of them regularly. Here's the short list, grouped by what they do.
Text structure
<h1>Top-level heading (one per page)</h1>
<h2>Section heading</h2>
<h3>Sub-section heading</h3>
<!-- h4, h5, h6 exist but you'll rarely go past h3 -->
<p>A paragraph of prose text.</p>
<strong>Importantly bold</strong> and <em>meaningfully italic</em>.
<!-- <b> and <i> also exist but <strong> and <em> are preferred because
they add meaning, not just visual styling. More on this below. -->
<br> <!-- a line break (self-closing, no content) -->
<hr> <!-- a thematic break — horizontal rule -->
Lists
<ul> <!-- unordered list -->
<li>Apples</li>
<li>Oranges</li>
<li>Bananas</li>
</ul>
<ol> <!-- ordered list -->
<li>Wake up</li>
<li>Eat breakfast</li>
<li>Write HTML</li>
</ol>
Links and images
<a href="https://example.com">External link</a>
<a href="/pricing">Internal link (relative URL)</a>
<a href="#faq">Jump to #faq anchor on this page</a>
<img src="/hero.png" alt="A photo of a laptop on a café table">
<!-- alt text is required for accessibility and SEO. Describe the image. -->
Grouping containers
<div>A generic block container. Avoid when you can use something semantic.</div>
<span>A generic inline container.</span>
Semantic layout regions (we'll cover these next)
<header>, <nav>, <main>, <article>, <section>, <aside>, <footer>
Those ten-ish groups cover nearly everything you'll write. When you hit something more specialised -- tables, video players, detail-summary toggles -- look it up on MDN. Don't try to memorise the whole spec.
Block vs Inline — The One Distinction That Trips Everyone Up
Elements fall into two groups that behave very differently in the layout.
Figure 2 — Every element is either block (takes its own line) or inline (flows within a line). Knowing which is which explains 80% of "why is my layout weird" questions.
Block elements start on a new line and take up the full width available. Stacking <p>s gives you paragraphs, one per line.
Inline elements sit inside a line of text. You can wrap a word in <strong> and the surrounding text flows around it naturally.
Rule of thumb: if it's a "thing" (a heading, a paragraph, a section), it's block. If it's "a bit of text with extra meaning" (a link, emphasis, an inline image), it's inline.
Semantic HTML — Giving Meaning, Not Just Looks
In the 90s, everyone built layouts with <table> tags. In the 2000s, it was giant piles of <div>s with class names like <div class="header">. HTML5 (2014) added semantic elements -- tags that describe what a region is, not how it looks.
| Element | Use for |
|---|---|
| <header> | The top of a page or section (logo, primary heading, top nav) |
| <nav> | A set of navigation links |
| <main> | The one unique, primary content region of the page |
| <article> | A self-contained piece of content (a blog post, a news story, a product card) |
| <section> | A thematic grouping of content, usually with a heading |
| <aside> | Content tangentially related to the main -- sidebars, pull quotes, ads |
| <footer> | The bottom of a page or section (copyright, secondary nav, contact info) |
A well-structured page uses them like this:
<body>
<header>
<h1>My Blog</h1>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<main>
<article>
<h2>Why I Moved Off Vercel</h2>
<p>Last week I migrated my SaaS from Vercel to Cloudflare…</p>
</article>
</main>
<aside>
<h3>Related</h3>
<ul>
<li><a href="/posts/d1-migration">D1 migration notes</a></li>
</ul>
</aside>
<footer>
<p>© 2026 William.</p>
</footer>
</body>
This is the same visual layout you'd get with <div class="header">, <div class="content">, etc. But it has three major advantages:
- Screen readers announce landmarks -- a blind user can jump straight to
<main>or the<nav>. - Search engines understand which part is the actual article vs the sidebar chrome, and they rank the main content.
- Future you can read the HTML six months later and immediately see the structure without hunting through class names.
Forms — How the Web Accepts Input
Almost every useful web app has a form somewhere: sign up, log in, search, contact, checkout. A form is just an HTML element that bundles up user input and sends it to a server. The server sends back a response, and the browser renders it -- same request/response loop from Chapter 1.
<form action="/signup" method="POST">
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
<label for="password">Password</label>
<input type="password" id="password" name="password" minlength="8" required>
<button type="submit">Create account</button>
</form>
Break that down:
<form>wraps everything.actionis the URL to send the data to;methodisGETorPOST(from Chapter 1's HTTP methods).<label for="email">associates a label with an input. When the user clicks the label, focus jumps to the input. Essential for accessibility.<input>is the actual field. Thetypeattribute picks the UI:text,email,password,number,date,file,checkbox,radio, and more. The browser gives you keyboard types, validation, and date pickers for free.nameis what the server sees as the key. The email ends up asemail=user@example.comin the request body.required,minlength,pattern,min,maxare built-in browser validations. No JS needed for basic form correctness.<button type="submit">triggers the submission.
Here's what happens when the user hits "Create account":
Figure 3 — The signup-form lifecycle. Built-in browser validation runs before the request ever reaches the server; a successful submit usually ends in a redirect to a "thank you" or dashboard page.
That's a full-page-reload form submission -- the oldest, simplest, most reliable pattern on the web. Modern apps often intercept submits with JavaScript to avoid the reload (we'll cover that in Chapter 5), but the underlying request is still the same POST you see above.
Input types worth knowing
| type= | What you get |
|---|---|
| text | Plain text (default) |
| email | Text + email keyboard on mobile + basic email validation |
| password | Masked characters |
| number | Numeric keyboard, spinner buttons |
| tel | Phone-number keyboard on mobile |
| date, time, datetime-local | Native date/time pickers |
| file | File chooser |
| checkbox, radio | Toggles |
| hidden | Invisible value sent with the form (CSRF tokens, IDs) |
| search | Text field with built-in clear button |
| url | URL-flavoured text field |
Using the right type is free UX -- mobile keyboards adapt, validation works out of the box, and password managers know what to fill.
Accessibility in Five Rules
Accessibility ("a11y" for short -- 11 letters between "a" and "y") is about making your pages work for everyone, including people using screen readers, keyboard-only navigation, or low-vision zoom. It's also SEO. Google's crawler and a screen reader use nearly the same signals.
You don't need to become an expert. Follow these five rules and you'll be ahead of 90% of the web.
- Use semantic tags. Every
<header>,<nav>,<main>,<footer>,<article>gives a screen reader a landmark to jump to. A page of nothing but<div>s is invisible to them. - Every image has
alt. If the image is informational, describe it:alt="Graph showing revenue doubled in Q3". If it's decorative (a background flourish), usealt=""(empty -- tells the screen reader to skip it). Never omit the attribute. - Every input has a
<label>. Use<label for="emailId">+<input id="emailId">. Never rely on placeholder text alone -- it disappears as soon as the user types, and screen readers often ignore it. - Don't skip heading levels. Go
h1 → h2 → h3, noth1 → h4. Screen readers build a document outline from headings; skipping levels breaks it. - Everything works with the keyboard alone. Tab through your page. Every button, link, and input should get focus in a logical order, and every interactive thing should respond to Enter or Space. If you built a fake button out of a
<div onclick=…>, it won't -- use<button>instead.
How the Browser Actually Reads Your HTML
One more mental model and we're done. When HTML arrives from the server, the browser doesn't wait for the whole file -- it parses streamingly, building the DOM as bytes arrive. Roughly:
Figure 4 — The HTML parsing pipeline. Characters become tokens, tokens become DOM nodes, DOM plus CSS becomes a render tree, and the render tree becomes pixels.
Two practical consequences of streaming parsing:
- The order of your HTML matters. The browser can start rendering the top of the page before the bottom has even arrived. Put critical content early.
<script>tags block parsing by default. When the parser hits a<script>withoutdeferorasync, it stops the DOM build, downloads and runs the script, then resumes. A slow script in<head>delays your whole page. Put scripts at the end of<body>, or use<script defer src="app.js">.
Exercise — Build Your First Real Page
Put a new file called index.html on your desktop with this content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hi, I'm a web developer</title>
</head>
<body>
<header>
<h1>Hi, I'm [your name]</h1>
<nav>
<a href="#about">About</a>
<a href="#projects">Projects</a>
<a href="#contact">Contact</a>
</nav>
</header>
<main>
<section id="about">
<h2>About</h2>
<p>I'm learning to build and ship web apps using the
<strong>simpleappshipper.com</strong> tutorials.</p>
</section>
<section id="projects">
<h2>Projects</h2>
<ul>
<li>Coming soon…</li>
</ul>
</section>
<section id="contact">
<h2>Contact</h2>
<form action="mailto:me@example.com" method="POST">
<label for="msg">Message</label>
<input type="text" id="msg" name="msg" required>
<button type="submit">Send</button>
</form>
</section>
</main>
<footer>
<p>© 2026. Built by me, with HTML.</p>
</footer>
</body>
</html>
Double-click the file. It opens in your browser. It's ugly -- no styling, cramped text, default blue underlined links -- but it's real HTML, rendered by a real browser, using every pattern from this chapter. Try Tab to move through the links and form. Open DevTools and click through the Elements tab to see the DOM tree you just built.
In Chapter 3, we'll make it not ugly with CSS.
Next Steps
You now know what HTML is, why it's a tree, the twenty-ish elements you'll use all the time, the block/inline split, semantic structure, forms, and the five accessibility rules. That's enough to build and ship a real -- if unstyled -- website today.
Next:
- Save the exercise page somewhere you can find it. We'll restyle it in Chapter 3.
- Browse a site you like, open DevTools, and look at its HTML structure. You'll start recognising the patterns -- header, nav, main, article. It demystifies the web fast.
- Move on to Chapter 3 — CSS Fundamentals, where you'll learn why the box model is the most important concept in all of design.
Ship your apps faster
When you're ready to publish your Swift app to the App Store, Simple App Shipper handles metadata, screenshots, TestFlight, and submissions — all in one place.
Try Simple App Shipper