💻 Computer Science · Undergraduate · CS 130

Web Development: HTML, CSS & JavaScript

A hands-on first course in building websites. Starting from how the web actually works, you will write real HTML for structure, CSS for design and layout, and JavaScript to make pages interactive, then combine all three into a small project you build yourself. Every lesson teaches the code on the page, so you can learn the whole craft for free at your own pace with nothing but a text editor and…

Start the interactive course (quizzes, progress, videos) →

Free forever. No sign-up, no ads. 15 lessons. The full lesson text is below so you can read it right here.

Module 1: How the Web Works

The client/server model, HTTP, URLs, and the three languages of the front end. You will build the mental model that every later lesson quietly depends on: the browser asks for text, receives text, and paints it into pages.

Clients, Servers, and HTTP

  • Describe the client/server model of the web.
  • Trace what happens between typing a URL and seeing a page.
  • Identify the roles of HTTP requests and responses.

Every time you open a web page, two computers have a short conversation. Your device runs a client - the web browser - and somewhere far away a server is a computer whose job is to store web pages and hand them out when asked. This back-and-forth is called the client/server model, and it underlies almost everything on the internet. The word "server" describes a role, not a size: it might be a giant data center or a spare laptop under someone's desk. What makes it a server is simply that it waits for requests and answers them.

What happens when you visit a page

Suppose you type an address into the browser and press Enter. Here is the sequence, simplified but honest:

  1. The browser reads the URL (the web address) and figures out which server to contact.
  2. The name in the URL, like example.com, is turned into a numeric IP address by a lookup system called DNS (the Domain Name System), which works like a phone book for the internet.
  3. The browser opens a connection to that server and sends an HTTP request: a short message that says, in effect, "please send me this page."
  4. The server prepares the page and sends back an HTTP response containing the content, usually HTML text.
  5. The browser reads that HTML, fetches any extra files it mentions (styles, images, scripts), and draws the finished page on your screen. Drawing the page is called rendering.

Notice that a single page is rarely one download. The HTML arrives first; it acts like a shopping list that names a stylesheet, some images, and a script. The browser then makes a fresh request for each of those. A typical news article can trigger dozens of requests before it looks finished. Understanding this explains why pages sometimes appear in stages: text first, then images popping in, then interactivity waking up.

Anatomy of a URL

A URL packs several pieces of information into one line. Take https://shop.example.com/products/shoes?color=red. The scheme is https (which protocol to use). The host is shop.example.com (which server). The path is /products/shoes (which resource on that server). Everything after the ? is the query string, a set of extra parameters like color=red that the server can use to tailor the response. Reading a URL as these parts, rather than as one opaque blob, makes the whole system feel far less mysterious.

HTTP: the language of the request

HTTP stands for HyperText Transfer Protocol. A protocol is just an agreed set of rules for how two computers talk. An HTTP request names a method (the kind of action wanted) and a path (which resource). The two you will meet first are GET, which asks to read something, and POST, which sends data to the server, such as a filled-in form. A request also carries headers, short labeled lines of extra information, such as which languages the browser prefers or what kind of content it can accept.

Every response comes with a numeric status code that reports how it went. You have almost certainly seen 404, which means the page was not found. A few worth knowing, grouped by their leading digit:

CodeFamilyMeaning
2002xx successOK - the request succeeded
301 / 3023xx redirectThe page lives at a different address
4044xx client errorNot Found - no such resource
4034xx client errorForbidden - you are not allowed in
5005xx server errorSomething broke on the server

The pattern is worth memorizing: 2xx means success, 3xx means "look elsewhere," 4xx means the client asked for something wrong, and 5xx means the server itself failed. Knowing the family tells you where to look when something goes wrong: a 4xx is usually your mistake, a 5xx is the server's.

The secure version

Modern sites use HTTPS, the same protocol with encryption added, so that data traveling between your browser and the server cannot be read by anyone in between. That is the padlock icon you see near the address bar. Encryption here does two jobs: it keeps the conversation private, and it verifies that the server really is who it claims to be, using a certificate issued by a trusted authority. As a developer you get HTTPS mostly for free; the important idea is that it protects the conversation without changing how you write pages.

Why this matters

Everything else in this course sits on top of this one exchange. When a page loads slowly, when a form fails to submit, or when an image is missing, you now have a map: is it a DNS lookup, a bad request, a slow server, a missing file? The key mental model to carry forward is simple - the browser is a program that asks servers for text, receives that text, and turns it into the pages you see. Every technique ahead is about writing that text well.

Key terms
Client
The program, usually a browser, that requests and displays web pages.
Server
A computer that stores web resources and sends them when requested.
HTTP
The protocol of rules browsers and servers use to exchange requests and responses.
URL
A web address that tells the browser what resource to fetch and from where.
DNS
The Domain Name System that translates a domain name into a numeric IP address.
Status code
A number in an HTTP response reporting the outcome, such as 200 or 404.
Header
A labeled line of extra information carried by an HTTP request or response.
HTTPS
HTTP with encryption that keeps the conversation private and verifies the server's identity.

HTML, CSS, and JavaScript: The Three Languages

  • Distinguish the roles of HTML, CSS, and JavaScript.
  • Set up a folder and files for a website.
  • Open an HTML file in a browser.

A modern web page is built from three separate languages, each with one clear job. Keeping their jobs separate is one of the most important habits a web developer can build, and it is a habit that pays off on your very first project and on the largest sites in the world alike.

The three roles

  • HTML (HyperText Markup Language) provides the structure and content: the headings, paragraphs, images, and links. Think of it as the bones and words of the page.
  • CSS (Cascading Style Sheets) provides the presentation: colors, fonts, spacing, and layout. Think of it as the clothing and paint.
  • JavaScript provides the behavior: what happens when the user clicks, types, or scrolls. Think of it as the muscles that make the page move.

A useful analogy is a house. HTML is the framing and walls, CSS is the paint and furniture, and JavaScript is the electricity that makes the lights and appliances respond when you flip a switch. You could live in a house with framing but no paint and no electricity; it would be plain but usable. That mirrors the web exactly: a page with only HTML still works, just without style or interaction.

Why separate the three?

This separation is called separation of concerns, and it is not mere tidiness. When structure, presentation, and behavior live in different places, you can restyle an entire site by editing one stylesheet without touching a single word of content. You can hand the HTML to a writer and the CSS to a designer and they will not collide. And when something breaks, you know where to look: a missing heading is an HTML problem, a wrong color is a CSS problem, an unresponsive button is a JavaScript problem. Mixing them, by contrast, produces pages that are painful to change and to debug.

The tools you need

You need only two free things you already have or can install: a text editor to write code and a web browser to view it. A plain editor works, though a code editor with syntax highlighting is nicer because it colors your tags and warns you about typos. You do not need to install a server for this course; a browser can open HTML files directly from your disk. That low barrier is one of the web's great gifts - the same technology that runs billion-dollar sites is fully available to a beginner with a laptop and no budget.

Setting up your first project

Create a folder for your site. Inside it, make three files. A common, sensible naming scheme is:

my-site/
    index.html     the page structure (HTML)
    styles.css     the design (CSS)
    script.js      the behavior (JavaScript)

The name index.html is special: when a browser or server is pointed at a folder, it looks for index.html as the default page. This is why you can visit a site's home page by typing just the domain, with no filename - the server quietly serves the index. To view your work, open index.html in a browser by double-clicking it or dragging it onto a browser window.

The edit-refresh loop

Every time you change the file and save, refresh the browser to see the update. That short loop, edit then refresh, is the heartbeat of front-end development. You will run it thousands of times: make a small change, save, glance at the result, and repeat. Keeping the change small each time is a genuine skill, because when something looks wrong you will know exactly which edit caused it. Throughout this course you will write code in these three files and watch the browser bring it to life. In the next module you will write the HTML that gives every page its shape.

Key terms
HTML
The markup language that defines the structure and content of a web page.
CSS
The language that controls the visual presentation of HTML: color, spacing, and layout.
JavaScript
The programming language that adds interactive behavior to a web page.
Text editor
A program for writing and editing plain-text code files.
index.html
The conventional default filename a browser loads when pointed at a folder.
Separation of concerns
The practice of keeping structure, presentation, and behavior in distinct places so each can change independently.

Module 2: HTML Structure and Semantics

Elements, tags, attributes, the document skeleton, and meaningful semantic markup. You will learn not just how to write valid HTML but how to write HTML that communicates meaning to browsers, search engines, and assistive technology.

Elements, Tags, and the Document Skeleton

  • Write valid HTML elements with opening and closing tags.
  • Build the standard HTML5 document skeleton.
  • Add attributes to elements.

HTML describes a page as a set of elements. An element is a piece of content wrapped in tags. Most elements have an opening tag and a closing tag, and the content sits between them. A closing tag looks just like the opening tag with a slash added.

<p>This is a paragraph.</p>

Here <p> is the opening tag, </p> is the closing tag, and the text between them is the content. The whole thing is a paragraph element. The letters p are the tag name, which tells the browser what kind of element this is. Tag names are not case sensitive, but the universal convention is lowercase, and following it keeps your files consistent and easy to read.

Nesting

Elements can contain other elements. This is called nesting, and it must be tidy: an element that opens inside another must also close inside it, like properly matched brackets.

<p>Reading is <strong>very</strong> rewarding.</p>

The <strong> element opens and closes entirely inside the paragraph, which is correct. Overlapping tags, where one element closes after another one opens, is invalid and confuses the browser. A helpful way to picture nesting is as a tree: the paragraph is a branch, and the <strong> is a twig growing from it. Every element has exactly one parent and can have many children, and this parent-child tree is the same structure JavaScript will later walk through as the DOM.

Attributes

An attribute adds extra information to an element. Attributes go inside the opening tag as name-value pairs, with the value in quotes. For example, the lang attribute states the page's language:

<html lang="en">

An element can carry several attributes at once, separated by spaces, and their order does not matter. Two attributes you will use on almost every project are id, which gives one element a unique name, and class, which tags one or many elements with a shared label. You will lean on both heavily when you reach CSS and JavaScript, because they are how you point at specific elements to style or manipulate them.

Empty elements

A few elements are empty (also called void): they hold no content and therefore have no closing tag. The line break <br> and the image <img> are common examples. Because there is nothing to wrap, writing <br></br> would be meaningless; the single tag is complete on its own.

The document skeleton

Every real HTML page starts from the same skeleton. Learn it by heart; you will type it at the top of every file you write.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My First Page</title>
  </head>
  <body>
    <h1>Hello, web!</h1>
    <p>This is my first page.</p>
  </body>
</html>

Reading it line by line: <!DOCTYPE html> declares the page as modern HTML5, and it must be the very first line so the browser uses its standard, predictable rendering rules. The <html> element wraps the whole document and is called the root. The <head> holds information about the page that is not shown in the main window: the <meta charset="UTF-8"> line declares the character encoding so accented letters and symbols display correctly, the viewport line sets up mobile behavior, and the <title> appears on the browser tab and in search results. The <body> holds everything the visitor actually sees.

Why the skeleton matters

It is tempting to skip parts of the skeleton because a browser will often display your content even if you leave some out. Resist that temptation. The missing DOCTYPE can throw the browser into a legacy "quirks mode" that renders your careful CSS differently, and a missing charset can turn curly quotes into garbled symbols. Typing the full skeleton every time is a small discipline that prevents a whole category of baffling bugs. Get comfortable with this shape; it is the foundation for everything ahead.

Key terms
Element
A unit of a web page, typically an opening tag, content, and a closing tag.
Tag
The markup like <p> or </p> that marks the start or end of an element.
Attribute
A name-value pair inside an opening tag that gives extra information about the element.
Nesting
Placing elements inside other elements with properly matched open and close tags.
Empty element
An element with no content and no closing tag, such as <br> or <img>.
head
The document section holding information about the page, like its title and character set.
DOCTYPE
The first line declaring the page as HTML5 and triggering the browser's standards mode.
id / class
Attributes that name a single element uniquely (id) or tag one or many elements with a shared label (class).

Semantic HTML and Text Elements

  • Choose text elements by meaning, not by appearance.
  • Use headings to build a clear document outline.
  • Lay out a page with semantic sectioning elements.

Good HTML is semantic, which means each tag is chosen for what the content means, not for how it happens to look. A heading is marked as a heading because it is a heading, not merely because you want big text. Semantic markup helps search engines understand your page and lets screen readers guide people who cannot see the screen. It is the difference between a document that merely appears organized and one that genuinely is.

Headings and paragraphs

HTML offers six heading levels, from <h1> (most important) down to <h6> (least). Use exactly one <h1> per page for its main title, then nest lower levels to show structure, much like an outline. Body text goes in paragraphs with <p>.

<h1>Chocolate Chip Cookies</h1>
<h2>Ingredients</h2>
<p>You will need flour, butter, sugar, and chocolate.</p>
<h2>Steps</h2>
<p>Mix the dry ingredients, then add the wet ones.</p>

The crucial rule is to keep heading levels in order and not skip them for visual effect. Do not jump from an <h2> straight to an <h4> just because you want smaller text; that breaks the outline that screen readers and search engines rely on. If the size is wrong, fix it with CSS, not by choosing the wrong level.

The document outline

Think of your headings as forming a table of contents that exists even when no one draws it. Assistive software lets a blind user pull up a list of a page's headings and jump straight to the section they want, exactly the way a sighted reader scans a page for a bold subtitle. A clean, correctly nested heading structure is therefore not decoration; it is a navigation system. A page whose headings read h1, h2, h2, h3, h2 tells a clear story, while one that scatters levels at random is disorienting to anyone who cannot see the visual layout.

Emphasis done right

To stress a word, use <strong> for strong importance (shown bold) and <em> for emphasis (shown italic). These carry meaning that assistive technology can announce, sometimes by changing the tone of the synthesized voice. There are also older look-alike tags, <b> and <i>, that produce the same bold and italic appearance but carry no meaning. Prefer <strong> and <em> when you mean importance or emphasis, and remember that if you only want visual styling, that is CSS's job, which you will meet soon.

Sectioning elements

Older sites wrapped everything in generic <div> boxes. Modern HTML gives us named regions that describe the page's anatomy:

  • <header> - introductory content, often a logo and site title.
  • <nav> - a set of navigation links.
  • <main> - the primary content, unique to this page. Use one per page.
  • <article> - a self-contained piece, like one blog post, that would make sense on its own.
  • <section> - a thematic grouping of related content, usually with its own heading.
  • <footer> - closing content, such as copyright or contact links.
<body>
  <header>
    <h1>My Blog</h1>
    <nav>
      <a href="index.html">Home</a>
      <a href="about.html">About</a>
    </nav>
  </header>
  <main>
    <article>
      <h2>My First Post</h2>
      <p>Welcome to my blog.</p>
    </article>
  </main>
  <footer>
    <p>Copyright 2026</p>
  </footer>
</body>

When to reach for div and span

The <div> and its inline cousin <span> still exist, and they are not forbidden - they are simply meaningless containers, useful for grouping when no semantic element fits, usually so you can attach styling or scripting to the group. The right instinct is to reach for the meaningful tag first and fall back to <div> only when nothing describes your content. A page built this way reads like a well-organized document, and that clarity pays off every time you style it, return to edit it, or hand it to someone using a screen reader.

Key terms
Semantic HTML
Markup chosen for the meaning of content rather than its appearance.
Heading
An element from <h1> to <h6> that titles a section and defines the document outline.
strong / em
Elements marking strong importance and emphasis, carrying meaning as well as bold or italic styling.
main
The semantic element wrapping a page's primary, unique content; used once per page.
nav
A semantic element grouping a set of navigation links.
div
A generic block container used to group content when no semantic element fits.
Document outline
The hierarchical structure of a page implied by its headings, used for navigation by assistive technology.
Accessibility
The practice of building pages usable by people with disabilities, supported heavily by semantic markup.

Module 3: Links, Images, Lists, and Tables

Connecting pages, showing images accessibly, and organizing content into lists and tables. These are the elements that turned a set of documents into a genuine, navigable web.

Lists and Tables

  • Build unordered and ordered lists.
  • Structure tabular data with rows, headers, and cells.
  • Choose lists versus tables appropriately.

Much of the content on the web is naturally a list or a grid, and HTML has purpose-built elements for both. Choosing the right one is another small act of semantics: it tells machines whether your content is a sequence of items or a matrix of related values.

Unordered and ordered lists

An unordered list, <ul>, is for items whose order does not matter; it shows bullet points. An ordered list, <ol>, is for sequences where order carries meaning; it shows numbers. In both, each item is a list item, <li>.

<ul>
  <li>Bread</li>
  <li>Cheese</li>
  <li>Apples</li>
</ul>

<ol>
  <li>Preheat the oven</li>
  <li>Mix the batter</li>
  <li>Bake for 20 minutes</li>
</ol>

The choice between them is not about the bullet versus the number; it is about meaning. Baking steps must happen in order, so they are an <ol>; a grocery list can be bought in any order, so it is a <ul>. If you later dislike how the markers look, CSS can change or remove them, so never pick the wrong list type just for appearance.

Nesting lists

Lists can be nested: place a whole <ul> or <ol> inside an <li> to make sub-items, which is how you build outlines and multi-level menus. Navigation menus are very often an unordered list of links, styled with CSS to look like a horizontal bar; underneath the styling, they are semantically just a list of destinations, which is exactly what a menu is.

<ul>
  <li>Fruit
    <ul>
      <li>Apples</li>
      <li>Bananas</li>
    </ul>
  </li>
  <li>Vegetables</li>
</ul>

Tables for real tabular data

A table displays data in rows and columns. Use one only for genuine tabular data, like a schedule or price list, never just to arrange a page (layout is CSS's job, as you will see). The core elements are:

  • <table> wraps the whole table.
  • <tr> is a table row.
  • <th> is a header cell (bold and centered by default).
  • <td> is a normal data cell.
<table>
  <tr>
    <th>Fruit</th>
    <th>Price</th>
  </tr>
  <tr>
    <td>Apple</td>
    <td>$1.00</td>
  </tr>
  <tr>
    <td>Banana</td>
    <td>$0.50</td>
  </tr>
</table>

Read the structure carefully: a table is a stack of rows, and each row is a series of cells. The browser lines the cells up into columns automatically because every row has the same number of cells in the same order. The first row here uses <th> so the browser and screen readers know those are column labels, not data.

Structuring larger tables

For larger tables you can group rows with <thead> for the header row and <tbody> for the body, and add a <caption> as the table's title. These groupings help both styling and accessibility: a screen reader can announce the caption and repeat column headers as a user moves across a wide table, so a blind user does not lose track of which column a number belongs to. Spanning cells across columns or rows is possible with the colspan and rowspan attributes, but the four elements above are enough to build almost any table you need.

Lists or tables?

A quick rule of thumb: if your content is a shopping list, use a list; if it is a spreadsheet, use a table. More precisely, use a table only when each item has the same set of attributes that you want to compare across items, like a price and a quantity for each product. If your items are just a sequence of single things, a list is simpler and more appropriate. Choosing correctly keeps your markup honest and your pages accessible.

Key terms
Unordered list (<ul>)
A bulleted list for items whose order does not matter.
Ordered list (<ol>)
A numbered list for items in a meaningful sequence.
List item (<li>)
A single entry inside a <ul> or <ol>.
Table (<table>)
An element that arranges genuine tabular data into rows and columns.
Table row (<tr>)
A horizontal row of cells within a table.
th / td
A header cell and a normal data cell inside a table row.
thead / tbody
Elements that group a table's header rows and body rows for styling and accessibility.

Module 4: Forms and User Input

Collecting information from visitors with inputs, labels, and controls. Forms are where a static page becomes a two-way conversation, powering searches, logins, checkouts, and surveys.

Building Forms

  • Assemble a form with labeled inputs.
  • Choose the right input type for each field.
  • Use select menus, checkboxes, and radio buttons.

A form is how a web page collects information from a visitor: a search box, a login, a survey, a checkout. It is the moment a page stops merely presenting information and starts listening. Everything goes inside the <form> element, which groups the controls and defines where the data should go when submitted.

<form action="/subscribe" method="post">
  ...form controls go here...
</form>

The action attribute names the server address to send the data to, and method is usually post for data that changes something. Recall from Module 1 that GET requests append data to the URL while POST tucks it into the request body; that is exactly the distinction here. For learning, the exact destination does not matter; focus on the controls inside, because those are what you assemble and label.

Inputs and labels

The workhorse control is <input>, an empty element whose type attribute decides what it collects. Always pair each input with a <label>. Connecting them, by matching the label's for attribute to the input's id, means clicking the label focuses the field and screen readers announce the field's purpose correctly.

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

The name attribute labels the data when it is sent to the server; without it, the value would arrive nameless and useless. So a fully wired input often carries three attributes at once: type for what it collects, id to pair with a label, and name to identify its value on submission. Common input types include:

typeCollects
textA single line of plain text
emailAn email address, with basic checking
passwordText hidden as dots
numberA numeric value, often with up/down arrows
dateA calendar date, often with a date picker
checkboxAn on/off choice
radioOne choice from a group

Why input types matter

Choosing the right type does far more than validation. On a phone, type="email" summons a keyboard with an @ key ready, type="number" shows a numeric keypad, and type="date" offers a friendly calendar rather than making the user type a date by hand. The right type also gives the browser a chance to catch obvious mistakes before the form is even sent, and it improves accessibility by telling assistive technology what kind of data each field wants. Reaching for the most specific type is a small choice that noticeably improves the experience.

Bigger text, menus, and grouped choices

For multi-line text use <textarea>. For a drop-down menu use <select> with <option> children. Radio buttons that share the same name become a group where only one can be chosen, which is exactly why the shared name matters: it is what ties them into a single choice.

<label for="msg">Message</label>
<textarea id="msg" name="msg"></textarea>

<label for="size">Size</label>
<select id="size" name="size">
  <option>Small</option>
  <option>Medium</option>
  <option>Large</option>
</select>

<p>Choose one:</p>
<label><input type="radio" name="plan" value="free"> Free</label>
<label><input type="radio" name="plan" value="pro"> Pro</label>

Note the difference between checkboxes and radios: checkboxes are independent on/off switches where any number can be ticked, while radios sharing a name are mutually exclusive, so choosing one clears the others. Reach for radios when exactly one answer is allowed and checkboxes when several are.

The submit button and basic validation

Finally, a button with type="submit" sends the form. You can write <button type="submit">Send</button>. Add the required attribute to any input the browser should refuse to submit while empty, and the browser will show a helpful message pointing at the missing field. Other built-in checks include minlength and maxlength for text and min and max for numbers. With these pieces you can build the login screens, contact forms, and surveys that power interactive sites, and you can do it with clean, accessible markup that works before a single line of JavaScript is added.

Key terms
Form
The <form> element that groups input controls and defines where their data is sent.
input
An empty element whose type attribute determines what kind of data it collects.
label
Text tied to an input via for and id, improving usability and accessibility.
name attribute
The identifier attached to a control's value when the form is submitted.
select / option
Elements that build a drop-down menu of choices.
required
An attribute that stops a form from submitting while the field is empty.
radio vs checkbox
Radios sharing a name allow exactly one choice; checkboxes are independent switches allowing any number.

Module 5: CSS Fundamentals

Selectors, the cascade, the box model, color, and typography. This module turns bare, unstyled markup into designed pages and teaches the rules that decide which style wins when several compete.

Selectors and the Cascade

  • Write CSS rules with selectors and declarations.
  • Target elements by tag, class, and id.
  • Attach CSS to an HTML page.

CSS styles your HTML. A CSS rule has two parts: a selector that chooses which elements to style, and a block of declarations that say how to style them. Each declaration is a property and a value separated by a colon and ended with a semicolon.

p {
  color: navy;
  font-size: 18px;
}

Read it as: "for every <p> element, set the text color to navy and the font size to 18 pixels." The curly braces hold the declarations; the selector p sits in front. Forgetting the semicolon at the end of a declaration is one of the most common beginner bugs, because it can silently break the declaration after it, so build the habit of always closing each line with one.

Three ways to select

The three selectors you will use constantly are:

  • A type selector targets all elements of a tag, like p or h1. Use it to set broad defaults.
  • A class selector targets any element carrying a given class attribute; write a dot before the name, like .warning. Classes are reusable across many elements and are the workhorse of real stylesheets.
  • An id selector targets the single element with a matching id; write a hash before the name, like #main-title. An id must be unique on the page.
.button {
  background: teal;
  color: white;
}
#logo {
  width: 120px;
}

In the HTML you attach these with attributes: <p class="warning"> or <h1 id="main-title">. You can also combine selectors. A descendant selector like nav a means "an <a> that is anywhere inside a <nav>," which lets you style navigation links without touching links elsewhere. You can group selectors with commas, so h1, h2, h3 { color: navy; } styles all three at once.

The cascade and specificity

The C in CSS is Cascading, which refers to how the browser resolves conflicts when several rules target the same element. It decides the winner by specificity: an id beats a class, and a class beats a type selector. If two rules have equal specificity, the one written later in the file wins. This ordering is what lets a broad rule set a sensible default while a more specific rule overrides it for special cases, and understanding it turns "why isn't my style applying?" from a mystery into a diagnosis.

A worked example of the cascade

Imagine this HTML and CSS together:

<p class="note" id="intro">Hello</p>

p       { color: black; }
.note   { color: green; }
#intro  { color: red; }

What color is the text? All three rules match the same paragraph, so specificity decides. The type selector p is the weakest, the class .note beats it, and the id #intro beats them both. The paragraph renders red. Now remove the id rule: the class wins and the text is green. Remove the class too, and only the type rule remains, so it is black. Walking down this ladder is exactly how you reason about any conflict you meet in practice.

Attaching CSS

The best way to add CSS is an external file linked from the head, so one stylesheet can control every page of a site at once:

<head>
  <link rel="stylesheet" href="styles.css">
</head>

You could also write CSS inside a <style> block in the head, or inline on a single element with a style attribute, but the external file is cleanest and keeps structure and presentation separate. Inline styles are also the most specific of all and hardest to override, which is another reason to avoid them. That separation of concerns is a habit worth keeping for the rest of your career, and the linked stylesheet is what makes it practical.

Key terms
Rule
A CSS selector plus the block of declarations applied to the matched elements.
Selector
The part of a CSS rule that chooses which elements to style.
Declaration
A single property-and-value pair, such as color: navy;
Class selector
A reusable selector written with a dot, matching elements with a given class attribute.
id selector
A selector written with a hash, matching the one element with that unique id.
Specificity
The ranking that decides which conflicting CSS rule wins, with id beating class beating type.
Descendant selector
A selector like 'nav a' that matches an element nested inside another.

The Box Model, Color, and Typography

  • Explain the four layers of the CSS box model.
  • Set colors with names, hex, and rgb.
  • Control fonts, size, and spacing of text.

Once you can select elements, the single most important CSS idea to master is the box model. Every element on a page is a rectangular box, and that box has four concentric layers, from the inside out. Almost every spacing and sizing question you will ever have comes back to these four layers, so it is worth learning them cold.

The four layers

  • Content - the text or image itself, with a width and height.
  • Padding - clear space inside the box, between the content and the border.
  • Border - a line drawn around the padding.
  • Margin - clear space outside the border, pushing other elements away.
The CSS box model: margin surrounds border surrounds padding surrounds content margin border padding content

A memorable way to remember the order: content is the picture, padding is the mat around it, the border is the frame, and the margin is the empty wall space between frames. Padding shares the element's background color, while margin is always transparent, which is a quick way to tell which one you are looking at when you inspect a page.

.card {
  width: 300px;
  padding: 16px;
  border: 2px solid gray;
  margin: 24px;
}

A worked example: the box-sizing trap

By default the width applies only to the content, so padding and border are added on top of it. Take the card above: its content width is 300px, but it has 16px of padding on each side and a 2px border on each side. The total visible width is therefore 300 + 16 + 16 + 2 + 2 = 336 pixels, not 300. This surprises nearly every beginner, and it wrecks layouts where you expected two 50%-wide boxes to sit side by side but they overflow because their padding pushed them past the space available.

The widely used fix is one declaration: box-sizing: border-box;. It changes the meaning of width so that it includes the padding and border. With it applied, a box you size at 300px stays exactly 300px wide, and the padding eats inward instead of pushing outward. Many developers apply it to everything at the very top of their stylesheet with a rule like * { box-sizing: border-box; }, and from then on sizing simply behaves the way intuition expects.

Color

CSS gives you several ways to name a color. Three common ones:

  • Keywords like red, navy, or tomato - convenient for quick work.
  • Hex codes like #1b4d8f, six digits giving the red, green, and blue amounts in base 16, from 00 to ff each.
  • rgb like rgb(27, 77, 143), the same three channels written as decimal numbers from 0 to 255.

Hex and rgb describe the exact same colors in different notation; #1b4d8f and rgb(27, 77, 143) are identical, because hex 1b is decimal 27, hex 4d is 77, and hex 8f is 143. A related form, rgba(...), adds a fourth value for opacity from 0 (invisible) to 1 (solid), which is how you make semi-transparent overlays.

body {
  background: #f4f4f4;
  color: rgb(30, 30, 30);
}

Typography

Text is styled with a small family of properties: font-family chooses the typeface (list fallbacks, ending in a generic family like sans-serif, so that if the first font is unavailable the browser tries the next); font-size sets the size; font-weight sets boldness; line-height sets the vertical space between lines; and text-align aligns text left, right, center, or justified.

body {
  font-family: Georgia, "Times New Roman", serif;
  font-size: 18px;
  line-height: 1.6;
}
h1 {
  font-weight: bold;
  text-align: center;
}

The comma-separated list in font-family is called a font stack, and the generic family at the end is a guaranteed fallback so text always renders in something reasonable. Comfortable body text is usually 16 to 18 pixels with a line-height around 1.5 to 1.6, which gives the eye enough room to move from one line to the next without losing its place. With the box model for spacing and these properties for color and type, you already control most of how a page looks.

Key terms
Box model
The layout model where every element is a box of content, padding, border, and margin.
Padding
Space inside an element, between its content and its border.
Margin
Space outside an element's border that separates it from other elements.
box-sizing: border-box
A setting that makes an element's width include its padding and border.
Hex code
A color written as a # followed by six base-16 digits for red, green, and blue.
line-height
The vertical spacing between lines of text, best set as a unitless multiplier.
Font stack
A comma-separated list of fonts ending in a generic family, providing fallbacks.

Module 6: Layout and Responsive Design

Arranging boxes with flexbox and grid, then adapting to every screen size. These modern tools replaced years of layout hacks and make it straightforward to build pages that look right on any device.

Flexbox: Laying Out in One Dimension

  • Turn an element into a flex container.
  • Distribute and align items along a row.
  • Wrap and space flex items.

For years, arranging boxes side by side in CSS was awkward, relying on floats and clearing tricks that were fragile and hard to reason about. Flexbox changed that. It is a layout system for placing items in a single direction, a row or a column, and controlling how they line up and share space. It is the right first tool for any layout that is essentially a line of things.

The container and its items

You switch on flexbox by setting display: flex; on a parent element. That parent becomes the flex container, and its direct children become flex items that lay out in a row by default. This parent/child relationship is the heart of flexbox: properties you set on the container control the whole group, while a few properties on the items fine-tune individuals.

.menu {
  display: flex;
}

With just that one line, the children of .menu sit next to each other instead of stacking vertically the way block elements normally do. Two container properties then do most of the work of positioning them.

The two axes: justify-content and align-items

Flexbox thinks in terms of two axes. The main axis runs in the direction items flow (across, for a row), and the cross axis runs perpendicular to it (down, for a row). justify-content controls spacing along the main axis, and align-items controls alignment across the cross axis. Keeping straight which property works on which axis is the single most useful thing to internalize about flexbox. Useful values for justify-content:

ValueEffect
flex-startItems packed to the start (the default)
centerItems centered as a group
space-betweenEqual gaps between items, none at the ends
space-aroundEqual space around each item
space-evenlyEqual space between items and at both ends
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

This is the classic navigation bar: logo on the left, links on the right, everything vertically centered. Read it as a sentence - spread the children apart along the row, and center them top to bottom. Centering something both horizontally and vertically, long the most infamous nuisance in CSS, is now just justify-content: center; paired with align-items: center;, a two-line answer to a question that once filled forums.

Direction, wrapping, and gaps

Change the direction with flex-direction: column; to stack items vertically instead of horizontally; note that this swaps the axes, so now justify-content works vertically and align-items horizontally. Allow items to flow onto new lines when they run out of room with flex-wrap: wrap;, which prevents a row of cards from squashing or overflowing on a narrow screen. Add even spacing between items with the gap property, a clean modern replacement for fiddly margins.

.cards {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}

Growing and shrinking items

Each child can also grow or shrink to fit the available space. Setting flex: 1 on the items tells them to share the leftover space equally, which is how you make three cards fill a row in equal thirds even as the window resizes. You can give different weights, so flex: 2 on one item and flex: 1 on another makes the first twice as wide. This flexibility - items that respond to the space they are given rather than demanding a fixed size - is where the name flexbox comes from, and it is what makes flex layouts adapt so gracefully. Flexbox is the right tool whenever your content is essentially a line of things; for two-dimensional grids, the next lesson introduces CSS grid.

Key terms
Flexbox
A CSS layout system that arranges items in a single row or column.
Flex container
An element with display: flex whose direct children become flex items.
justify-content
The property that distributes flex items along the main axis (the row).
align-items
The property that aligns flex items across the container (the cross axis).
flex-wrap
A property that lets flex items flow onto multiple lines when space runs out.
gap
A property that sets even spacing between flex or grid items.
flex shorthand
The property bundling flex-grow, flex-shrink, and flex-basis to control how an item sizes.

CSS Grid: Two-Dimensional Layout

  • Define a grid with rows and columns.
  • Size tracks with fr units and repeat().
  • Place content into a grid.

Where flexbox arranges items in one direction, CSS grid arranges them in two at once: rows and columns together. It is the most powerful layout tool in CSS and is ideal for page skeletons and card galleries, anywhere you need things to line up both across and down. Learning it alongside flexbox gives you a complete layout toolkit.

Turning on the grid

Set display: grid; on a container, then define its columns with grid-template-columns. Each value you list defines one column's width, and the number of values sets how many columns there are. The container's direct children then become grid items, just as they become flex items under flexbox.

.gallery {
  display: grid;
  grid-template-columns: 200px 200px 200px;
}

That makes three columns each 200 pixels wide. Items you place inside flow into the cells automatically, filling the first row left to right, then wrapping to the next, so you rarely have to position anything by hand for a simple gallery.

The fr unit and repeat()

Fixed pixel columns are rigid and do not adapt to screen size. The grid-specific fr unit (short for fraction) divides the available space into shares. Three columns of 1fr 1fr 1fr split the width into equal thirds that stretch and shrink with the screen, which is exactly what you usually want. Because typing the same value repeatedly is tedious, repeat() is a shorthand:

.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

You can mix units freely within one template, which is where grid shines for page structure. A common page layout is a fixed sidebar next to a flexible main area:

.layout {
  display: grid;
  grid-template-columns: 250px 1fr;
}

Here the sidebar is always 250px and the main content takes all the remaining space, because 1fr means "one share of whatever is left" after the fixed 250px is subtracted. Add grid-template-rows the same way to control row heights, though rows often size themselves to their content automatically, which is usually the sensible default.

Spanning cells and placing items

By default each item occupies one cell, but items can span several. Writing grid-column: span 2; on an item makes it stretch across two columns, which is how you build a featured card that is wider than its neighbors or a header that runs the full width above a two-column body. More advanced placement lets you name lines and address exact cells, but spanning alone handles a large share of real designs. Grid's ability to make one item cover a rectangle of cells, while others flow around it, is something flexbox simply cannot do.

Grid or flexbox?

A simple guide: reach for flexbox when you are laying out along one line, like a navigation bar or a row of buttons, where the items and their content decide the sizing. Reach for grid when you need rows and columns to line up together, like a photo gallery or a full page layout, where you want to define the structure first and drop content into it. The distinction is content-driven versus layout-driven. They cooperate happily, and it is extremely common to use grid for the overall page skeleton and flexbox inside individual pieces such as a card's header. Between them, these two tools handle nearly every layout you will ever need, and they do it responsively and without the hacks that dominated web layout for its first two decades.

Key terms
CSS grid
A layout system that arranges items into rows and columns simultaneously.
grid-template-columns
The property that defines the number and size of a grid's columns.
fr unit
A grid unit representing a fraction of the available space.
repeat()
A shorthand that repeats a track definition a given number of times.
grid gap
The spacing between grid rows and columns, set with the gap property.
grid item
A direct child of a grid container that occupies one or more cells.
minmax()
A grid function setting a track to flex between a minimum and maximum size.

Responsive Design and Media Queries

  • Explain why responsive design matters.
  • Write media queries that adapt to screen width.
  • Use relative units and the viewport meta tag.

People visit websites on phones, tablets, laptops, and large monitors, and today more traffic comes from phones than from desktops on many sites. Responsive design is the practice of building one page that reshapes itself to look good on any screen, rather than making separate sites for each device. One codebase serves everyone, which is both cheaper to maintain and kinder to users.

The viewport meta tag

Responsive design starts in the HTML head with one line you already saw in the skeleton:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

Without it, phones pretend to be a wide desktop, around 980 pixels, and shrink the whole page to fit, making text tiny and forcing users to pinch and zoom. This tag tells the browser to use the device's real width and not to zoom out, so your responsive styles actually take effect. It is easy to forget and its absence is a classic reason a "responsive" site still looks broken on a phone, so never leave it out.

Relative units

Fixed pixel sizes do not adapt to different screens or user preferences. Responsive layouts therefore lean on relative units:

  • Percentages size an element relative to its parent, so width: 50% is always half the container, whatever the container's size.
  • rem sizes things relative to the root font size, so setting the root size once scales the whole page together, and it respects a user who has increased their browser's default text size for readability.
  • Viewport units like vw and vh are a percentage of the window's width or height, useful for full-screen sections.

Preferring these units over fixed pixels is a large part of what makes a layout flow rather than break. Pixels still have their place for things like borders, but for widths and text sizes, relative units keep a design flexible.

Media queries

The heart of responsive design is the media query: a block of CSS that applies only when a condition about the screen is true, most often its width. You write @media, a condition in parentheses, then a set of rules in braces that take effect only while the condition holds.

/* base styles: single column for phones */
.cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

/* on wider screens, switch to three columns */
@media (min-width: 700px) {
  .cards {
    grid-template-columns: repeat(3, 1fr);
  }
}

Read it carefully. The base rule gives one column, which is right for a narrow phone. The media query says: when the screen is at least 700 pixels wide, use three columns instead. Below 700px the query is dormant and the base rule stands; at 700px and above it activates and overrides the column count. The width at which the layout changes is called a breakpoint.

A worked example of a breakpoint decision

Suppose a phone is 375px wide, a tablet 800px, and a laptop 1300px, and you have written the media query above. Trace each: the phone at 375px is below 700, so the query does not fire and the visitor sees one column. The tablet at 800px is above 700, so the query fires and the visitor sees three columns. The laptop at 1300px is also above 700, so it too sees three columns. One breakpoint has produced two sensible layouts from a single stylesheet. Add a second query, perhaps at 1100px, to introduce a fourth column for very wide screens, and you tune the design further without ever duplicating the page.

Mobile-first thinking

Writing the simple phone layout first and adding complexity for larger screens with min-width queries is called the mobile-first approach. It tends to produce cleaner, faster pages because the smallest devices get the leanest styles by default and never download rules meant for large screens they will not use. You can layer several breakpoints, for example one at 700px for tablets and another at 1100px for wide desktops, so a single stylesheet gracefully serves every visitor from a small phone to a large monitor. Combined with the flexible units and the grid and flexbox tools from earlier lessons, mobile-first media queries let you build genuinely universal pages.

Key terms
Responsive design
Building one page that adapts its layout to any screen size.
Viewport meta tag
The head tag that tells mobile browsers to use the device's real width.
Media query
A CSS block that applies only when a screen condition, such as a minimum width, is met.
Breakpoint
A screen width at which the layout changes.
rem
A unit relative to the root font size, letting a whole page scale together.
Mobile-first
Designing the small-screen layout first, then enhancing it for larger screens.
Container query
A newer query letting an element adapt to the size of its own container rather than the viewport.

Module 7: JavaScript and the DOM

Variables, functions, events, and manipulating the page to make it interactive. This module gives static pages a pulse, letting them respond to clicks, keystrokes, and form input in real time.

JavaScript Basics: Variables and Functions

  • Declare variables and use core data types.
  • Write and call functions.
  • Add JavaScript to a page.

JavaScript is the programming language of the browser. Where HTML and CSS describe a page, JavaScript lets it act: respond to clicks, change content, validate a form, fetch new data. This lesson covers the language basics; the next connects them to the page. Unlike HTML and CSS, JavaScript is a full programming language with variables, decisions, and logic, so this is where you begin to program rather than describe.

Variables

A variable is a named container for a value. Modern JavaScript declares variables with let (for values that can change) or const (for values that will not). Prefer const unless you know the value must change, because a name that cannot be reassigned is easier to reason about and prevents a whole class of accidental bugs.

let score = 0;
const name = "Ada";
score = score + 10;

Statements end with a semicolon. Note that const stops you from reassigning the name to a different value; it does not freeze the contents of an object or array the name points to, a subtlety you will appreciate later. There is an older keyword, var, which you will see in legacy code but should avoid in new code because its scoping rules are surprising and error-prone.

Data types

The core data types you will use are numbers (42, 3.14; JavaScript has just one number type for both whole and decimal values), strings (text in quotes), and booleans (true or false). A template literal, written with backtick quotes, lets you drop variables directly into text using a dollar sign and braces, though double-quoted strings joined with the + operator also work. Two special values, null and undefined, both represent "no value," with undefined being what you get from a variable that has not been assigned yet.

Functions

A function is a reusable block of code you define once and run whenever you call it by name. Functions can take parameters (inputs) and return a result. They are the primary way to organize code and avoid repetition, following the principle of writing a piece of logic once and reusing it.

function greet(person) {
  return "Hello, " + person + "!";
}

let message = greet("Sam");

Calling greet("Sam") runs the function with person set to "Sam" and hands back "Hello, Sam!", which we store in message. The value handed back by return is the function's result; if a function has no return, it produces undefined. Many modern examples use a shorter arrow function form, which is especially handy for tiny functions:

const square = (n) => n * n;
let result = square(5);   // 25

Making decisions

An if statement runs code only when a condition is true, and an optional else handles the other case. Comparisons use === for "equal to", !== for "not equal", and > and < for greater and less than. Prefer the three-character === over the two-character ==, because === compares without surprising type conversions, whereas == can judge the number 0 equal to the string "0" and cause subtle bugs.

if (score > 50) {
  message = "You passed!";
} else {
  message = "Try again.";
}

Running your code

Attach a script to your page with a <script> tag just before the closing </body>, so the page has loaded before the code runs and can find the elements it needs:

  <script src="script.js"></script>
</body>

While learning, console.log(...) prints a value to the browser's developer console (opened with the F12 key), which is invaluable for checking what your code is actually doing at each step, rather than guessing. When something behaves unexpectedly, logging the values along the way is usually the fastest path to understanding. With variables, functions, and conditions you now have the grammar of the language; next you will point it at the page and make things happen on screen.

Key terms
Variable
A named container for a value, declared with let or const.
const
A declaration for a variable whose value will not be reassigned.
Function
A reusable, named block of code that can take inputs and return a result.
Parameter
A named input a function receives when it is called.
if statement
A control structure that runs code only when a condition is true.
console.log
A command that prints a value to the browser's developer console for debugging.
=== (strict equality)
An operator that compares value and type without converting, preferred over ==
Arrow function
A concise function syntax written with =>, common for small functions.

The DOM, Events, and Interactivity

  • Select and change page elements with the DOM.
  • Respond to user actions with event listeners.
  • Build a small interactive feature.

When the browser loads your HTML, it builds a live, in-memory model of the page called the DOM (Document Object Model). The DOM represents every element as an object your JavaScript can read and change, arranged in the same parent-child tree as your nested tags. Change the DOM, and the page updates instantly on screen. This is the bridge between the language you just learned and the page you see, and it is where web development becomes visibly interactive.

Selecting elements

You reach into the page through the global document object, your entry point to everything on the page. The most flexible selector is querySelector, which takes a CSS selector string - the very same selector syntax you learned for styling - and returns the first matching element.

const title = document.querySelector("#main-title");
const firstButton = document.querySelector(".btn");

Because it accepts any CSS selector, one method covers ids, classes, tags, and combinations, so it is worth making your default. Use querySelectorAll to get every match as a list you can loop over with a for...of loop, for example to attach the same behavior to many buttons. Once you hold an element, you can read it, change it, or listen to it.

Changing content and style

Two properties do most everyday work. textContent reads or sets an element's text, and style lets you set CSS properties, written in camelCase because hyphens are not allowed in property names, so background-color becomes backgroundColor and font-size becomes fontSize.

title.textContent = "Welcome back!";
title.style.color = "teal";

Setting styles directly is fine for one-off changes, but the cleaner habit for anything reusable is to toggle CSS classes with element.classList.add("active"), remove("active"), or toggle("active"). This keeps the actual styling in your stylesheet where it belongs and lets JavaScript simply switch states on and off, which scales far better than scattering colors and sizes through your script.

Events

An event is something that happens on the page: a click, a key press, a form submission, the mouse moving. You react to one by registering an event listener with addEventListener. You give it the event name and a function to run when it fires, and, as the previous lesson's deep dive explained, the browser calls that function back at the right moment.

const button = document.querySelector("#say-hi");
button.addEventListener("click", function () {
  alert("Hello there!");
});

Now every click on that button runs the function. The function you pass is often called the handler or callback. Common event names are click, input (fired repeatedly as the user types in a field), and submit (for forms, where you often call the event's preventDefault() method to stop the page from reloading so you can handle the data in JavaScript).

Putting it together: a counter

Here is a complete, correct example. The HTML has a display and a button; the JavaScript makes clicking the button raise a count.

<p id="count">0</p>
<button id="add">Add one</button>

<script>
  let total = 0;
  const display = document.querySelector("#count");
  const addBtn = document.querySelector("#add");

  addBtn.addEventListener("click", function () {
    total = total + 1;
    display.textContent = total;
  });
</script>

Trace it step by step: a variable total starts at 0; we grab the two elements and store them in constants so we do not have to search for them on every click; and we register a click listener on the button. Each click runs the handler, which adds one to total and writes the new number into the paragraph using textContent, and the browser redraws that paragraph at once. Notice that total lives outside the handler so its value persists between clicks - if it were declared inside, it would reset to 0 every time.

Why this pattern matters

That small loop - an event changing a variable that updates the DOM - is the essence of interactive web pages. A to-do list, a shopping cart, a game score, a live search: all of them are elaborations of exactly this cycle of listen, update state, reflect the state back into the DOM. Every major framework you might learn later, such as React or Vue, exists largely to manage this same cycle more conveniently as pages grow, but the underlying idea is the one you can now build by hand.

Key terms
DOM
The Document Object Model, the browser's live object representation of the page.
document
The global object that is your JavaScript entry point into the page.
querySelector
A method that returns the first element matching a CSS selector string.
textContent
A property for reading or setting the text inside an element.
Event
Something that happens on the page, such as a click, keypress, or form submit.
addEventListener
The method that registers a function to run when a given event fires.
classList
An element property with add, remove, and toggle methods for switching CSS classes.
Event delegation
Handling events for many child elements with one listener on a shared parent, using event bubbling.

Module 8: Build a Page - Capstone Project

Combine HTML, CSS, and JavaScript into one small, complete, interactive page. This is where every earlier idea comes together in something you build and can show off.

Project: A Personal Profile Card

  • Plan a small page combining all three languages.
  • Assemble semantic HTML, responsive CSS, and a JavaScript interaction.
  • Test and refine your finished page.

Everything so far has been practice for this: building a complete page yourself. Your project is a personal profile card - a small page with your name, a short bio, a list of interests, and one interactive button. It is deliberately modest so you can actually finish it and see all three languages cooperate, which is far more valuable than a sprawling project you abandon half-built. Every large site is assembled from exactly these parts, so finishing this small one teaches the real craft in miniature.

Planning before coding

A moment of planning saves a lot of flailing. Decide the three layers up front: the structure is a heading, a bio paragraph, an interests list, a button, and an empty paragraph for the button to fill; the style is a centered white card with comfortable padding on a soft background; and the behavior is a click that reveals a fun fact. Naming your goal in terms of structure, style, and behavior is itself the separation-of-concerns habit at work, and it maps directly onto the three files you will edit.

Step 1: The HTML structure

Start from the document skeleton and fill the body with semantic elements. Keep it simple and meaningful, choosing tags for what the content means.

<body>
  <main class="card">
    <h1>Jordan Lee</h1>
    <p class="bio">A student learning to build the web.</p>
    <h2>Interests</h2>
    <ul>
      <li>Photography</li>
      <li>Cycling</li>
      <li>Reading</li>
    </ul>
    <button id="fact-btn">Show a fun fact</button>
    <p id="fact"></p>
  </main>
  <script src="script.js"></script>
</body>

Notice the choices: <main> wraps the primary content, a single <h1> names the person, an unordered list holds the interests because their order does not matter, and the empty <p id="fact"> is a target the JavaScript will fill. The script sits just before </body> so the elements exist by the time it runs.

Step 2: The CSS design

Style the card with the box model, color, and typography, and center it on the page with flexbox. A generous max-width keeps it comfortable on large screens while width: 100% lets it shrink on small ones.

body {
  font-family: Georgia, serif;
  background: #eef2f5;
  display: flex;
  justify-content: center;
  padding: 40px 16px;
}
.card {
  background: white;
  max-width: 400px;
  width: 100%;
  padding: 28px;
  border: 1px solid #ccc;
  border-radius: 10px;
}
h1 {
  color: #1b4d8f;
  margin-top: 0;
}
button {
  background: #1b4d8f;
  color: white;
  border: none;
  padding: 10px 16px;
  border-radius: 6px;
  cursor: pointer;
}

This one stylesheet exercises much of the course: flexbox centers the card, the box model gives it padding and a border, border-radius rounds its corners, the color and font properties set its look, and cursor: pointer signals that the button is clickable. The pairing of max-width: 400px with width: 100% is a small but real piece of responsive design - the card is at most 400 pixels but shrinks to fit a narrow phone rather than overflowing it.

Step 3: The JavaScript interaction

Make the button reveal a fun fact when clicked, using the DOM and an event listener, exactly the listen-update-reflect cycle from the last module.

const button = document.querySelector("#fact-btn");
const fact = document.querySelector("#fact");

button.addEventListener("click", function () {
  fact.textContent = "I once cycled 100 miles in a single day!";
});

The script selects the button and the empty paragraph, then listens for clicks on the button; each click writes the fact into the paragraph via textContent, and the browser shows it at once. It is only a few lines, but it is the same mechanism behind every interactive feature on the web.

Step 4: Test and refine

Open the page, click the button, and confirm the fact appears. Resize the window narrow and wide to check the card adapts, and open the developer console with F12 to make sure there are no errors. Then make it yours: change the name, bio, and interests, adjust the colors, and add a second interaction if you like, perhaps a button that toggles a dark background by adding a class with classList.toggle. Work in the small edit-save-refresh loop the whole way, changing one thing at a time so any mistake is easy to trace. When it works and reads cleanly, step back and appreciate what you have done: you have built a real web page from the ground up - structure in HTML, design in CSS, behavior in JavaScript. That is the whole craft in miniature, and every large site is built from these same parts, just more of them.

Key terms
Capstone project
A culminating exercise that combines the skills from an entire course.
max-width
A CSS property capping how wide an element can grow, useful for responsive cards.
border-radius
A CSS property that rounds the corners of an element's box.
cursor: pointer
A style that shows the hand cursor, signaling an element is clickable.
Integration
Combining HTML, CSS, and JavaScript so structure, style, and behavior work together.
Static site
A site made of files served unchanged, with no server-side program, and the easiest kind to publish.

Open the interactive version with quizzes and progress →