A short history, the moving parts of the platform, and the toolkit on your machine.
Thousands of researchers, and every document sits on a different system on a different machine. Tim Berners-Lee's proposal: documents that point to each other — a web of linked pages.
HyperText Markup Language — describes what a document contains: headings, paragraphs, links.
HyperText Transfer Protocol — "give me this URL" → "here is the document".
Uniform Resource Locator — one address that makes every document reachable.
1990 — he builds the first browser (named WorldWideWeb) and the first
server at CERN · 1991 — the first website goes live at
info.cern.ch
The first browser people actually use — it shows images next to text, and the web stops being an academic tool. Much of its team goes on to found Netscape (1994).
document.all.hero // exists only in Internet Explorer document.layers.hero // exists only in Netscape
HTML gets stretched past its job:
<font> tags for color, tables drawn
to fake page layout. Presentation is mixed into structure.
<p class="title">Hello</p>
.title { color: red; }
Structure stays in HTML, looks move to CSS — Separation of Concerns.
Click a button: nothing happens. A page is a poster that happens to be on a screen — no behavior of its own.
Brendan Eich builds the first version at Netscape in about 10 days.
button.onclick = () => alert("Clicked!")
Pages start reacting to the user — documents become applications.
the skeleton — content & meaning
the dressing — layout, color, spacing
the brain & muscle — logic & reaction
Gmail (2004), Google Maps (2005) — users post, upload, comment.
Structure that holds everything up — headings, paragraphs, images, buttons. Take it away and the body collapses.
Clothes, colors and personality — how the body presents itself. Same skeleton, totally different style.
Thinks, decides, reacts and moves — behavior. It turns a body that just stands there into a living person.
Ask the class: a site with only HTML is a skeleton. Only CSS — clothes on the floor. Only JS — a brain in a jar. You need all three.
Open it three ways
Panels to know
Free at code.visualstudio.com. Built-in Emmet expands
"!" into a full HTML5 boilerplate —
type it, press Tab.
Live Server (auto-reload browser) · Prettier (formats your code) · Auto Rename Tag (keeps tag pairs in sync).
One folder per lab, with the three core files: index.html, styles.css, script.js.
Right-click index.html → "Open with Live Server". Save a file → the browser refreshes itself.
<h1>, save, and watch it appear
without touching F5. If that loop works, your workflow is ready.
webdev-lab/ ├── index.html ← structure ├── styles.css ← styling └── script.js ← behavior Tip: open the FOLDER (not the file) in VS Code — IntelliSense then understands the whole project.
The web's structure language: tags, attributes, and meaning.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My first page</title>
</head>
<body>
<h1>Hello, class!</h1>
<p>My very own page.</p>
</body>
</html>
In VS Code, type ! and press Tab — Emmet
writes this whole boilerplate for you.
<h1>Main heading</h1> <h2>Section heading</h2> <p>Normal <strong>bold-important</strong> and <em>emphasised</em> text.</p> <p>Water is H<sub>2</sub>O,</p> <p>and 2<sup>10</sup> = 1024.</p> <p>Press <kbd>Ctrl</kbd> + <kbd>S</kbd> to save.<br>New line,<br>same paragraph.</p>
RENDERED RESULT
Main heading
Section heading
Normal bold-important and emphasised text.
Water is H2O, and 210 = 1024.
Ctrl
+
S
to save.
New line,
same paragraph.
↑ <hr> — a thematic break between topics
<ul>
<li>Morning: HTML drills</li>
<li>Afternoon: CSS layouts
<ul>
<li>Flexbox</li>
<li>Grid</li>
</ul>
</li>
</ul>
<ol start="3">
<li>Register the domain</li>
<li>Deploy the site</li>
</ol>
Global attributes — every element
ARIA — when HTML alone isn't enough
<nav>
<a href="index.html">Home</a>
<a href="pages/about.html">About</a>
<a href="https://developer.mozilla.org"
target="_blank" rel="noopener">MDN</a>
<a href="#contacts">Contacts ↓</a>
</nav>
<!-- ...page content... -->
<section id="contacts">
<h2>Contact us</h2>
</section>
My first website
You are on the home page.
About
Same server — the browser just asked for a different file.
Contact us
hello@mysite.dev
A page from the whole web — opened in its own tab.
<figure>
<img src="images/campus.jpg"
alt="Students on orientation day"
width="320" height="180"
loading="lazy">
<figcaption>
Orientation day, Sept 2026
</figcaption>
</figure>
<!-- always: folder path, alt text, -->
<!-- and explicit dimensions -->
<label> Email <input type="email" placeholder="you@univ.edu"> </label> <label> Password <input type="password"> </label> <input type="checkbox" checked> Remember me <input type="radio" name="year"> Year 1 <input type="radio" name="year"> Year 2 <label> Deadline <input type="date"> </label> <textarea rows="3"></textarea> <button>Sign up</button>
your editor — semantic.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Notes That Stick</title> </head> <body> <!-- retype the article here --> </body> </html>
result — live preview
h1 · h2 ×2 · strong · em · ol · ul
The web's presentation language: from colors and fonts to full page layouts.
Three ways to attach CSS — and the one to use:
INLINE
inside the HTML tag
Avoid — mixes jobs, can't be reused
INTERNAL
inside <head> of the page
Fine for quick single-page demos
EXTERNAL ✓
a separate .css file
The professional default: reuse + caching
| Pattern | Type | Matches |
|---|---|---|
| h1 | element | every <h1> on the page |
| .card | class | every element with class="card" |
| #header | id | the one element with id="header" |
| nav a | descendant | links inside <nav> — parents first, space, child |
| a:hover | pseudo-class | a link the mouse is currently over |
| h1, p | grouped | same style to h1 AND p in one rule |
When rules fight — specificity decides:
.card {
color: #1f2430;
background: #eaf3fa;
font-family: Arial, sans-serif;
font-size: 16px;
line-height: 1.6;
text-align: center;
border-radius: 16px;
box-shadow: 8px 8px 0 #e6e6e6;
transition: transform .3s;
}
.card:hover {
transform: scale(1.05);
}
WRITING COLORS — FOUR NOTATIONS, ONE BLACK
Orientation Day
Join us on campus — tours at 10:00 and 14:00.
hover me → grows smoothly (transition)
transition + :hover = your first animation, three lines, no JavaScript. The hover shadow above is box-shadow: 8px 8px 0 #E6E6E6— a hard offset, the minimalist's shadow.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Notes That Stick</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Notes That Stick</h1>
<p>Study Skills 101 · Week 4 handout</p>
<h2>Write it by hand</h2>
<p>Handwriting forces your brain to
<strong>summarize</strong>, not copy.</p>
<h2>Review in three passes</h2>
<ol>
<li>Same evening — rewrite the messy parts</li>
<li>Next day — turn headings into questions</li>
<li>End of week — explain it to a friend</li>
</ol>
<h2>What to capture</h2>
<ul>
<li>Key terms, with your own definition</li>
<li>One worked example per idea</li>
<li>Questions you still have</li>
</ul>
</body>
</html>
index.html carries Lab 1's content, styles.css dresses it — the preview always shows the two files together.
Total width surprises beginners:
default width + padding
+ border
= wider than you asked
* { box-sizing: border-box; }
put this in every project — width then includes padding + border, which is what you always meant anyway.
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
1fr = one fraction of the free space. Play with the controls — the code on the left is always exactly what the grid on the right is doing.
one row OR column · content decides size
rows AND columns · layout decides size
click a control — six items reflow, the CSS above updates with it
The five properties you actually need
| static | default — normal flow; offset properties are ignored |
| relative | stays in flow, can be nudged — anchors absolute children |
| absolute | removed from flow; pinned to the nearest positioned ancestor |
| fixed | pinned to the viewport — cookie banners |
z-index decides who is on top
higher = closer to the viewer. Only positioned (or flex/grid) items stack.
click a layer to bring it to the front
<!-- in <head>: the magic line --> <meta name="viewport" content="width=device-width, initial-scale=1" >
/* adapt at breakpoints */
@media (max-width: 600px) {
.menu {
flex-direction: column;
}
h1 {
font-size: 28px;
}
}
Typed values are real CSS — a valid one moves the pond, a made-up one gets flagged. Experiment freely; Reset puts it back.
Typed values are real CSS — a valid one moves the pond, a made-up one gets flagged. Experiment freely; Reset puts it back.
Typed values are real CSS — a valid one moves the pond, a made-up one gets flagged. Experiment freely; Reset puts it back.
Typed values are real CSS — a valid one moves the pond, a made-up one gets flagged. Experiment freely; Reset puts it back.
Typed values are real CSS — a valid one moves the pond, a made-up one gets flagged. Experiment freely; Reset puts it back.
The web's programming language: from variables to manipulating the page itself.
JavaScript is the browser's programming language. It reacts to people, changes the page, and can request data without a full reload.
The elements a page is made of.
How those elements look and lay out.
What happens when a person clicks, types, or waits.
const button = document.querySelector("#save");
button.addEventListener("click", () => {
document.body.classList.toggle("saved");
});
Think of it as a set of instructions that the browser runs after the HTML and CSS arrive. The next slides teach the building blocks of those instructions.
console.log() prints text, values,
and objects in the browser's developer console. Use it to check an
assumption before guessing at a bug.
const student = { name: "Ada", score: 10 };
console.log("Hello, web!");
console.log(student.score); // 10
console.log(student); // inspect the whole object
// console.log prints whatever is between the parentheses
console.log("Hello");
console.log(1 + 2);
console.log("Points:", 10); // two arguments: label + value
// comma keeps both on one line — plus adds numbers first
console.log("Score:", 10 + 5);
// your turn: log something about you
console.log("My name is ...");
try: change 1 + 2
into "1" + 2 — spot the difference?
console — live output · reruns as you type
| Keyword | Reassign? | Redeclare? | Scope | When to use |
|---|---|---|---|---|
| const | no | no | block | DEFAULT choice — values that never change |
| let | yes | no | block | values that WILL be reassigned (score, counters) |
| var | yes | yes | function | legacy 1995–2015 — you'll meet it in old code; don't write it |
const courseName = "Web Development"; // fixed let score = 0; // will change score = score + 10; // now 10
// three ways to declare — meet var, let, const
var one = "One";
let two = "Two";
const three = "Three";
one = "1"; // var — reassign, no complaint
two = "2"; // let — reassign, no complaint
// three = "3"; // ← uncomment me: const refuses
console.log(one, two, three);
// block scope: let stays inside the curly braces
{
var insideVar = "var leaks out of the block";
let insideLet = "let stays in the block";
}
console.log(insideVar); // works
// console.log(insideLet); // ← uncomment me: ReferenceError
try: uncomment both trap lines — the console shows real errors in red.
console — live output · reruns as you type
7 primitives …
| string | "Ada" | text in quotes — or backticks |
| number | 42 · 3.14 | integers and decimals, one type |
| boolean | true · false | the two logic values |
| undefined | not set | "no value yet" |
| null | empty | "we cleared it on purpose" |
| bigint | 9007…993n | astronomically big integers |
| symbol | Symbol("id") | rare — unique identifiers |
… plus one composite:
console.log(typeof "hello"); // string
console.log(typeof 42); // number
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof null); // object !
console.log(typeof [1, 2, 3]); // object
console.log(typeof {a: 1}); // object
// objects bundle many values:
const student = { name: "Ada" };
// string
let name = "Somchai";
console.log(`Hello, ${name}`); // template literal
console.log(typeof name); // "string"
// number
let price = 99.5;
console.log(price * 2); // 199
// boolean
let isStudent = true;
console.log(typeof isStudent); // "boolean"
// object — data in key: value pairs
let person = { name: "Somying", age: 30 };
console.log(person.name); // dot access
console.log(person["age"]); // bracket access
// array — an ordered list
let fruits = ["apple", "banana", "orange"];
console.log(fruits[0]); // "apple"
console.log(fruits.length); // 3
try: add ahobbies
array to person, then log
person.hobbies[0].
console — live output · reruns as you type
Operators you'll use daily
| + - * / | the classics: 7 * 6 → 42 |
| % | remainder: 7 % 2 → 1 — even/odd, cycles |
| ** | power: 2 ** 10 → 1024 |
| += -= *= ++ | shortcuts: score += 10 ≡ score = score + 10 |
| + on strings | "5" + 2 → "52" — JS glues instead of adds! |
const price = 19.99, qty = 3;
const total = price * qty; // 59.97
total.toFixed(2); // "59.97"
Math.round(4.6); // 5
Math.floor(4.9); // 4
Math.ceil(4.1); // 5
// random die: 1..6
Math.floor(Math.random() * 6) + 1;
const msg = `Total: ${total}`; // template
let grade;
if (score >= 50) {
grade = "pass";
} else if (score >= 40) {
grade = "retake";
} else {
grade = "fail";
}
// many exact values? use switch:
let off;
switch (day) {
case "Sat":
case "Sun": off = true; break;
default: off = false;
}
// if / else — pick one branch
let score = 45;
if (score >= 50) {
console.log("pass");
} else {
console.log("fail"); // ← this one runs
}
// else if — checks in order, stops at the first hit
let grade = 75;
if (grade >= 80) console.log("grade A");
else if (grade >= 70) console.log("grade B"); // ← runs
else console.log("grade F");
// && means both, || means either
let username = "admin";
let password = "1234";
if (username === "admin" && password === "1234") {
console.log("login ok");
} else {
console.log("wrong username or password");
}
try: set score to
80 and password to "0000" — which lines run
now?
console — live output · reruns as you type
press Run — i starts at 0, one chip per turn
// counted: 0, 1, 2, 3, 4
for (let i = 0; i < 5; i++) {
console.log("Cube " + i);
}
// walk an array: for...of
for (const c of colors) show(c);
let fruits = ["apple", "banana", "orange"];
// classic for — you control the index
for (let i = 0; i < fruits.length; i++) {
console.log(i, fruits[i]);
}
// for...of — the values, no index
for (let fruit of fruits) {
console.log(fruit);
}
// forEach — array method with a callback
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`);
});
// map — build a NEW array from the old one
let numbers = [1, 2, 3, 4];
let doubled = numbers.map((n) => n * 2);
console.log(doubled); // [2, 4, 6, 8]
try: add "mango" to
the list — predict all three loops before you look.
console — live output · reruns as you type
const colors = ["red", "green", "blue"];
colors[0]; // "red"
colors.length; // 3
colors[1] = "lime"; // replace
colors.push("gold"); // add to end
colors.pop(); // remove from end
colors.includes("red") // true
colors.join(" · "); // "red · lime · blue"
numbers = [42, 7, 19, 3, 25]
press a method — watch what happens to the array
const student = {
name: "Ada", year: 2, gpa: 3.86,
skills: ["html", "css"],
};
student.name; // "Ada" (dot)
student["gpa"]; // 3.86 (bracket)
student.skills.push("js");
// arrays of objects = a mini database:
const students = [
{ name: "Ada", year: 2 },
{ name: "Bo", year: 3 },
];
students[1].name; // "Bo"
// declaration: name + parameters
function calcArea(width, height) {
const area = width * height;
return area; // hand back
}
// call it with arguments:
const a = calcArea(3, 4); // 12
const b = calcArea(10, 2); // 20
// arrow function — same idea, shorter:
const calcArea2 = (w, h) => w * h;
calcArea2(5, 5); // 25
On a calculator page, every key calls the SAME function with a different argument.
// declaration — a recipe you can reuse
function greet(name) {
return `Hello, ${name}`;
}
console.log(greet("Somchai"));
console.log(greet("Somying"));
// arrow function — same idea, shorter
const shout = (text) => text.toUpperCase();
console.log(shout("quiet please"));
// your turn: write double(n) that returns n * 2,
// then log double(21) on the line below
try: log double(21)
first — the console shows the error until
double exists.
console — live output · reruns as you type
// 1 select the button
const btn = document.querySelector("#go");
// 2 listen for events
btn.addEventListener("click", () => {
// 3 change what is on screen
const title = document.querySelector("h1");
title.classList.toggle("done");
// 4 create & attach new content
const p = document.createElement("p");
p.textContent = "Clicked!";
document.body.append(p);
});
The user directory = all four moves — fetch the data, then build one element per user
fetch() is built into JavaScript. It
makes an HTTP request, waits for the response, then lets your page use the
data — here, a free test API that returns 10 fake users.
async function loadUsers() {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users");
if (!response.ok) throw new Error("Request failed");
const users = await response.json();
console.log(users);
}
loadUsers().catch(console.error);
each user: { name, email, address: { street, city, … } }
Ask for the URL with fetch().
Check whether the server accepted the request.
Read the JSON — an array of 10 user objects, ready to render.
name and
address
async function getUsers() {
const res = await fetch(
"https://jsonplaceholder.typicode.com/users");
const users = await res.json();
// TODO: replace this return — use .map() to build
// [{ name, address }] like the example on the right
return users;
}
getUsers().then((users) => console.log(users));
stuck?
console.log(users[0]) first and look at the
shape of one user.
console — live output · reruns as you type
expected: [{ name: "Leanne Graham", address: "Kulas Light, Apt. 556, Gwenborough 92998-3874" }, …]
preview — index.html + style.css
console — script.js
your move: finish the TODO in
script.js — the preview fills with ten real
users, no page reload. Stuck?
console.log(users[0]) first.
Structure it with HTML. Style it with CSS. Make it think with
JavaScript.
Then practice until it feels like handwriting.
KEEP THESE OPEN FOREVER
developer.mozilla.org
flexboxfroggy.com — all 24 levels
freecodecamp.org — the full free curriculum
caniuse.com — browser support
Questions?