A short history, the moving parts of the platform, and the toolkit on your machine.
Browser market share — Aug 2026
Every engine renders HTML, CSS and JS — write to standards, not to one browser.
SOURCE — STATCOUNTER GLOBALSTATS, AUG 2026
One page, three languages — three jobs:
the skeleton — content & meaning
<h1>Welcome</h1> <p>Course intro</p>
the skin — layout, color, spacing
h1 { color: navy; }
p { margin: 8px; }
the muscles — logic & reaction
button.onclick =
() => alert("Hi!");
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
<link> and
<script>
HOUSEHOLD NAMES YOU WILL MEET LATER
<!-- structure --> <h1>My portfolio</h1> <!-- presentation: my own stylesheet --> <link rel="stylesheet" href="styles.css"> <!-- behavior: my own script --> <script src="script.js"></script> <!-- a library, fetched from a CDN --> <script src="https://cdn.example.com/jquery.js"> </script>
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>
<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>
Lab 1 · Convert a Word article to HTML5
| Title / Heading 1 | → | <h1> |
| Heading 2 | → | <h2> |
| Body paragraph | → | <p> |
| Bold text | → | <strong> |
| Italic text | → | <em> |
| Bullet list | → | <ul> + <li> |
| Numbered steps | → | <ol> + <li> |
Workflow
<nav>…</nav> <section id="ch-1"> <h2>Chapter one</h2> <p>Retyped, not pasted.</p> </section>
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:
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.
.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: 0 8px 24px
rgba(0,0,0,.15);
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: 6px 6px 0 #E6E6E6 — a hard offset, the minimalist's shadow.
The five properties you actually need
rows × columns at once — place items in cells
.gallery {
display: grid;
grid-template-columns:
repeat(3, 1fr);
gap: 16px;
}
1fr = one fraction of the free space — three of them make three equal columns. Change 3 → 2 and the layout reflows — the same trick you will reuse for photo galleries.
one row OR column · content decides size · navbars, button rows
rows AND columns · layout decides size · page shells, galleries, calculators
They compose: grid for the page skeleton, flexbox inside each cell.
| static | default — normal flow; offset properties are ignored |
| relative | stays in flow, can be nudged (top/left) — and anchors absolute children |
| absolute | removed from flow; pinned to the nearest positioned ancestor |
| fixed | pinned to the viewport — stays put while scrolling (cookie banners) |
.cube {
position: relative;
z-index: 1;
}
.cube:hover { z-index: 5; }
z-index decides who is on top
higher = closer to the viewer. Only positioned (or flex/grid) items stack.
<!-- in <head>: the magic line -->
<meta name="viewport"
content="width=device-width,
initial-scale=1">
/* styles.css: adapt at breakpoints */
@media (max-width: 600px) {
.menu { flex-direction: column; }
h1 { font-size: 28px; }
}
/* rule one: experiment in DevTools
first, then copy the winner here */
body {
background: linear-gradient(
160deg, #ffffff, #e9e9e9 60%);
}
/* same spot, other options: */
/* background: #f0f0f0; */
/* background: url(paper.jpg); */
Orientation Day
The Lab 1 article, now wearing its first CSS.
Lab 3 · Draw with CSS, then animate
/* a cube is a square + shadow */
.cube {
width: 120px; aspect-ratio: 1;
background: #111;
border-radius: 12px;
box-shadow: 8px 8px 0 #d9d9d9;
transition: transform .3s;
}
.cube:hover { transform: rotate(8deg); }
/* the bubble's tail is a triangle */
.bubble::after { content: "";
position: absolute; bottom: -12px;
border: 12px solid transparent;
border-top-color: #111; }
Typed values are real CSS — even a wrong one moves the pond. Experiment freely; Reset puts it back.
Typed values are real CSS — even a wrong one moves the pond. Experiment freely; Reset puts it back.
Typed values are real CSS — even a wrong one moves the pond. Experiment freely; Reset puts it back.
Typed values are real CSS — even a wrong one moves the pond. Experiment freely; Reset puts it back.
Typed values are real CSS — even a wrong one moves the pond. Experiment freely; Reset puts it back.
The web's programming language: from variables to manipulating the page itself.
| 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
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" };
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
if (score >= 50) {
grade = "pass";
} else if (score >= 40) {
grade = "retake";
} else {
grade = "fail";
}
// many exact values? use switch:
switch (day) {
case "Sat":
case "Sun": off = true; break;
default: off = false;
}
// counted: 0, 1, 2, 3, 4
for (let i = 0; i < 5; i++) {
console.log("Cube " + i);
}
// unknown count: while
let hp = 100;
while (hp > 0) { hp -= 25; }
// walk an array: for...of
for (const c of colors) show(c);
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"
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.
// 1 select the button
const btn = document.querySelector("#go");
// 2 listen for events
btn.addEventListener("click", () => {
// 3 change what is on screen
const cube = document.querySelector(".cube");
cube.classList.toggle("spin");
// 4 create & attach new content
const p = document.createElement("p");
p.textContent = "Clicked!";
document.body.append(p);
});
The calculator = moves 1–3 · the cube farm = move 4, in a loop
const keys = document.querySelectorAll(".key");
const display = document.querySelector("#screen");
keys.forEach(k =>
k.addEventListener("click", () =>
display.value += k.textContent));
equalBtn.addEventListener("click", () =>
display.value = compute(display.value));
// compute(): parse left, op, right → return result
The brief: fill the stage with 100 cubes and a conversation of bubbles — by changing DATA, never by duplicating HTML.
// 1 the data — one object per cube
const cubes = [
{ color: "#111", size: 90, x: 20 },
{ color: "#7a7a7a", size: 60, x: 140 },
{ color: "#c9c9c9", size: 120, x: 230 },
];
// 2 the loop — one recipe, N elements
for (const cfg of cubes) {
const div = document.createElement("div");
div.className = "cube";
div.style.background = cfg.color;
div.style.width = cfg.size + "px";
div.style.left = cfg.x + "px";
stage.append(div);
}
Deliverable: a page that renders 100+ styled elements from a single array.
Convert a real Word article into a clean, semantic HTML5 page.
Style the page: solid color, gradient — first taste of CSS.
Draw a cube and a message bubble in pure CSS, then animate them.
5 levels of a game that drills justify-content and flex-direction.
A working calculator: buttons, display, and the JS behind them.
Generate 100 cubes & bubbles from an array — no copy-paste.
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?