// web · html · css

Just enough HTML and CSS: the document every server sends

Every page you have ever seen is one HTML document dressed by CSS rules — and everything you build later on this path generates that document. This guide teaches you to read and hand-write both languages: three simulators turn markup into a tree, let selectors pick their targets, and pull an element's four rectangles apart.

mindmap — quick refresh HTML + CSS — the document every server sends one page, three languages HTML = structure · CSS = appearance · JS = behavior the jobs never mix — swap the CSS, same document JS deferred: this path renders on the server anatomy of a tag element = open tag + content + close tag attributes are name="value" in the open tag void elements (img, br) have no close tag nesting is containment — last opened, first closed the fifteen tags h1–h6 · p · a href · img src alt ul/ol/li · table/tr/th/td div/span = plain boxes · strong/em · form/input skeleton: doctype → html → head → body selectors + declarations rule = selector + declarations element / .class / #id selectors ~10 properties carry a beginner external stylesheet via link tag wins the cascade later rules win among equals more specific wins overall: element < class < id the box model content → padding → border → margin padding pushes content in, margin pushes neighbors away background fills padding, never margin skip for now flexbox/grid · extra semantic tags JavaScript · frameworks

Prerequisites: none — this guide stands on its own. An editor helps for the type-along parts: Your toolbox sets one up in ten minutes. Time: ~60 minutes.

Before you write a single tag, three hard facts about the web:

  1. The web runs on exactly three languages, and they never trade jobs. HTML describes structure — "this is a heading, this is a list of two items". CSS describes appearance — "headings are dark red, 32 pixels". JavaScript describes behavior — "when this is clicked, do that". Every website you have ever used, however enormous, is these three and nothing else.
  2. Every page you have ever seen is one HTML document. Right-click any page and hit View Page Source: that text is the entire delivery. The server sent it, your browser drew it. By the end of this guide you'll be able to read most of what you find there.
  3. Browsers never error on bad HTML. There is no syntax-error screen for markup — the HTML standard requires browsers to guess their way through mistakes and render something anyway. That's why twenty-year-old sloppy pages still work, and why the browser will never tell you your markup is wrong. You have to write it right on your own.

One page, three languages

The three languages stay separate on purpose. HTML says what things are, never how they look — there is no "make this red" tag. CSS says how things look, and can restyle a document completely without touching a line of its markup. Keeping the jobs apart means a designer can rework the look while the structure stands still, and one stylesheet can dress a thousand pages.

On this learning path we defer the third language. JavaScript runs in the browser, after the page arrives — and our path is server-side rendering: programs on the server build the HTML, and the browser's only job is to display it (Server-side vs client-side rendering is the full argument). Every later stage — the SSR labs, forms, Django templates — has you generating HTML with code. You can't debug a program whose output you can't read, so first you learn to hand-write what your programs will later write for you.

What the browser does with the finished document — parsing it, laying it out, painting pixels — is its own machinery, covered in How browsers work. Here we only care about writing the document it receives.

Anatomy of a tag

Almost everything in HTML is an element, and an element has three parts — an open tag, content, and a close tag:

<p class="intro">Hello, web.</p>
  • <p ...> is the open tag: an angle-bracketed name that says what kind of thing starts here.
  • class="intro" is an attribute — extra information as name="value" pairs, always inside the open tag, values always quoted.
  • Hello, web. is the content — text, other elements, or both.
  • </p> is the close tag: same name, leading slash, no attributes.

A few elements can't contain anything, so they skip the content and close tag entirely — these are void elements. <img> (an image is its attributes) and <br> (a line break) are the two you'll meet first.

The one load-bearing rule is nesting: an element opened inside another must close inside it too. Last opened, first closed — like brackets in arithmetic. <p><strong>text</strong></p> is properly contained; <p><strong>text</p></strong> is not. This matters because the browser doesn't keep your document as text: it converts the markup into a tree of nodes, where "opened inside" becomes "is a child of". Get the nesting wrong and the browser silently guesses a tree you didn't intend (fact 3 above — no error, just a guess).

Watch that conversion happen. Here's the snippet the simulator parses:

<h1>Momo Menu</h1>
<p>The <strong>best</strong> dumplings.</p>
<ul>
  <li>steamed</li>
  <li>fried</li>
</ul>

Hold onto the mental model from that run: markup is a tree written as text. Indentation in your file is for humans; the nesting is the structure.

The fifteen tags that carry the web

HTML defines over a hundred elements. You need about fifteen (counting families as one) to write — and read — the overwhelming majority of pages:

TagJob
<h1><h6>Headings, six ranks. One <h1> per page; pick by rank, not by size — size is CSS's job.
<p>A paragraph. The workhorse of body text.
<a href="url">A link — the tag the web is named after (anchor). href is where it goes.
<img src="url" alt="text">An image. Void. src is the file; alt is the text shown (and read aloud) when the image isn't.
<ul>, <ol>, <li>A bulleted (unordered) or numbered (ordered) list; every item is an <li> nested inside.
<table>, <tr>, <th>, <td>A table: <tr> rows containing <th> header or <td> data cells. For data, never for page layout.
<div>A generic block box: takes the full width, stacks below whatever came before. A styling hook with no meaning of its own.
<span>A generic inline box: wraps a few words within a line, for styling part of a sentence.
<strong>, <em>Importance (renders bold) and stress (renders italic). Meaning first — the rendering is just the default CSS.
<form>, <input>The browser's built-in way to send data. One line here because they get a whole guide: How HTML forms work.

Those tags live inside a fixed skeleton — the minimal complete page, worth memorizing:

<!doctype html>            <!-- first line, always: "this is modern HTML" -->
<html lang="en">           <!-- the root element — everything nests inside it -->
<head>                     <!-- information ABOUT the page; nothing here is displayed -->
  <meta charset="utf-8">   <!-- how the bytes decode to text; always include it -->
  <title>My first page</title>  <!-- the browser-tab label and the search-result title -->
</head>
<body>                     <!-- everything the user actually sees -->
  <h1>Hello</h1>
  <p>My first hand-written page.</p>
</body>
</html>

The split to internalize: head is about the page, body is the page.

Type it along — typing, not pasting, is where the syntax sinks in. Open your editor, type the skeleton, and save it as page.html in a new folder. Double-click the file (or drag it onto a browser window): the title appears in the tab, the heading on the page. Now break it on purpose — delete a close tag, misspell an element — reload, and notice the browser renders something every time, without complaint. That silence is why hand-written discipline matters.

CSS: selectors and declarations

CSS is a list of rules, and every rule has the same two-part shape — a selector (which elements) and a block of declarations (what to set), each declaration a property: value pair ending in a semicolon:

h1 {
  color: #d6001c;
  font-size: 32px;
}

Three kinds of selector carry you a very long way:

  • Element selectorp — matches every <p> on the page, wherever it sits.
  • Class selector.note — matches every element with class="note", regardless of tag. Classes are reusable labels you invent; an element can carry several.
  • Id selector#title — matches the one element with id="title". An id must be unique on the page.

Selectors run against the tree you met in the last section. Here's the document body the simulator matches against:

<body>
  <h1 id="title">Momo Menu</h1>
  <p class="note">Open daily.</p>
  <p>Steamed or fried.</p>
  <div>
    <p class="note">No delivery on Saturdays.</p>
    <span class="note">Cash only.</span>
  </div>
</body>

About ten properties cover most of what a beginner styles:

PropertyControls
colortext color
backgroundthe element's background color
font-familythe typeface, with fallbacks: Georgia, serif
font-sizetext size, usually in px
marginempty space outside the element
paddingspace inside, between content and border
borderthe visible edge: 1px solid black
widthhow wide the element's box is
max-widtha ceiling on width — the readable-text trick below
text-alignleft, center, right

Where do the rules live? Three places work, one wins. You can put a style="..." attribute on a single element (inline), or a style element in the head — both exist, both get messy fast. The right home is an external stylesheet: one .css file, linked from the head, cached by the browser, shared by every page of the site:

<link rel="stylesheet" href="style.css">

Type-along, part two. Add that link line inside your page's head, then create style.css next to page.html:

body {
  font-family: Georgia, serif;
  max-width: 640px;
  margin: 0 auto;
  padding: 24px;
}

h1 {
  color: #d6001c;
}

Reload. The text snaps to a readable column (max-width caps the line length; margin: 0 auto centers the capped box) and the heading turns red. Same document, new dress — that's the separation of concerns doing its job.

The cascade, in one idea

The cascading in Cascading Style Sheets answers one question: when two rules set the same property on the same element, who wins? The whole answer, at beginner scale: the more specific selector wins; among equally specific rules, the later one wins. Specificity has exactly the ranking you'd guess — an element selector like p is the least specific, a class like .note beats it, an id like #title beats them both. So p { color: gray } and .note { color: red } can both match the same paragraph, in any order, and the text is red — you watched that play out in the simulator above. That's the entire mental model you need for now; the formal scoring system can wait until a real conflict sends you looking for it.

The box model

To CSS, every element is a rectangle — even a line of text, even a link. And each rectangle is really four nested ones, inside out: the content, wrapped in padding, wrapped in a border, wrapped in margin. Padding is space inside the border — the element's own breathing room, filled by its background. Margin is space outside the border — transparent, pushing the neighbors away. This single diagram resolves most "why is there a gap there?" confusion a beginner hits, so it gets a simulator:

The lesson to keep: padding pushes your own content in; margin pushes your neighbors away. Gap inside the colored area — adjust padding. Gap between two elements — adjust margin. And remember the browser ships default margins (that's the space around your untouched h1 and p), so the mystery gap you didn't write usually came from the default stylesheet.

What you can skip (for now)

Honest triage — each of these is real, none of it is needed yet, and chasing them now is the classic way to stall:

  • Flexbox and Grid — CSS's layout systems for placing boxes side by side. They matter the day a page needs columns or a navigation bar; your first pages stack top-to-bottom just fine.
  • Semantic tags beyond the fifteen (header, nav, article, footer, …) — better labels for page regions, good for accessibility and search engines. Adopt them once the skeleton is second nature; they nest by exactly the rules you already know.
  • JavaScript — the behavior language. It enters this path only after you can generate pages on the server; the SSR-vs-CSR guide explains why it's last, not first.
  • Frameworks — React, Vue, Tailwind, Bootstrap. Every one of them outputs the HTML and CSS you just learned; none of them makes sense before you can read what they produce.

Takeaways

  • Three languages, three jobs, never mixed: HTML is structure, CSS is appearance, JavaScript is behavior — and this path defers JavaScript because the server renders our pages.
  • An element is open tag + content + close tag, attributes ride in the open tag as name="value", and a few void elements (img, br) have no close tag at all.
  • Nesting is the load-bearing rule: last opened, first closed. Markup is a tree written as text, and the browser silently guesses a repair when you get it wrong — it will never error at you.
  • Fifteen tag families and one skeleton (doctypehtmlheadbody) cover most pages on the web. Be able to type the skeleton from memory.
  • A CSS rule is a selector plus declarations, and conflicts resolve by specificity — element < class < id — with later rules winning ties.
  • Every element is four rectangles: content, padding, border, margin. Padding pushes your content in; margin pushes your neighbors away.

References

  • MDN Web Docs. (n.d.). CSS basics. Mozilla. Retrieved August 22, 2026, from https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Styling_the_content
  • MDN Web Docs. (n.d.). HTML basics. Mozilla. Retrieved August 22, 2026, from https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Creating_the_content
  • MDN Web Docs. (n.d.). Specificity. Mozilla. Retrieved August 22, 2026, from https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascade/Specificity
  • MDN Web Docs. (n.d.). The box model. Mozilla. Retrieved August 22, 2026, from https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Box_model
  • WHATWG. (2026). HTML living standard. https://html.spec.whatwg.org/

The tag selection and page skeleton follow MDN's Your first website module (CC-BY-SA); the prose, mistakes, and simulators are ours.