Learning Goals
- Explain the roles of HTML (structure), CSS (presentation), and JavaScript (behavior).
- Apply separation of concerns with clean HTML/CSS/JS files.
- Understand the DOM, CSSOM, and Render Tree mental model.
- Build a triad mini-project with a theme toggle and local storage.
- Avoid common anti-patterns (inline styles and inline event handlers).
Part 1 — The Big Idea
Web pages are best built in layers so each concern stays focused:
What each layer does
- HTML: structure & semantics — headings, paragraphs, links, forms, landmarks.
- CSS: presentation — layout, spacing, colors, typography, responsive rules.
- JavaScript: behavior — events, dynamic updates, data fetching, state.
Key idea: HTML should be useful even without CSS/JS. CSS makes it beautiful; JS makes it interactive.
What not to mix
- Don’t style with HTML attributes or deprecated tags (e.g.,
<font>
). - Don’t wire events inline (e.g.,
onclick="..."
) — prefer JS listeners.
Part 2 — Where Each Layer Fits
HTML
All the content and semantic structure: <header>
, <main>
, <article>
, <nav>
, <button>
…
CSS
Visuals and layout: color schemes, spacing systems, grid/flex, fonts, animations.
JS
Behavior and state: event listeners, UI toggles, fetching data, saving preferences.
Part 3 — The Anatomy of a Triad Page
This minimal triad demonstrates each layer in its proper place.
<!DOCTYPE html> <!-- modern mode -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Triad Example</title>
<link rel="stylesheet" href="styles.css"> <!-- CSS layer -->
</head>
<body>
<header>
<h1>Hello, Triad!</h1>
<button id="themeBtn">Toggle Theme</button>
</header>
<main>
<article>
<h2>Why separation of concerns?</h2>
<p>Structure, style, and behavior live in their own layers.</p>
</article>
</main>
<script src="app.js" defer></script> <!-- JS layer -->
</body>
</html>
Part 4 — Terms & Mental Model
HTML → Parser → DOM Tree
CSS → Parser → CSSOM Tree
DOM + CSSOM → Render Tree → Layout → Paint → Composite
JS can read/modify DOM & CSSOM and trigger reflow/paint when needed.
Part 5 — Build Your Layered Page (Step-by-Step)
- Create a folder lesson-02.
- Create index.html, styles.css, and app.js.
- Copy the HTML from Part 3 into index.html.
- Add base styles and a theme modifier in styles.css.
- Implement a theme toggle in app.js and store the preference with
localStorage
. - Open index.html in the browser and test.
Starter CSS
:root { color-scheme: light dark; }
body { font-family: system-ui, sans-serif; margin: 0; line-height: 1.6; padding: 1.25rem; }
header { display: flex; gap: .75rem; align-items: center; justify-content: space-between; }
button { padding: .5rem .75rem; border: 1px solid #ccc; border-radius: .5rem; background: #f4f4f4; cursor: pointer; }
main { max-width: 60ch; }
.dark body { background: #121212; color: #eaeaea; }
.light body { background: #ffffff; color: #222; }
Starter JS
const btn = document.getElementById('themeBtn');
const saved = localStorage.getItem('theme');
if (saved) document.documentElement.className = saved;
btn.addEventListener('click', () => {
const root = document.documentElement;
const next = root.classList.contains('dark') ? 'light' : 'dark';
root.className = next;
localStorage.setItem('theme', next);
});
Part 6 — Open, Refresh, and Inspect
- Open index.html in your browser (double-click or drag to a tab).
- Edit files, save (Ctrl/Cmd + S), and refresh.
- Right-click → Inspect → Elements & Computed to see HTML and final styles.
Tip: If the toggle “does nothing”, confirm the script is loaded with
defer
and check the Console for errors.Part 7 — Common Anti-Patterns
❌ Inline styles
<p style="color:red;font-size:24px">Hi</p>
Why it hurts: hard to reuse or override; mixes concerns.
✅ Use classes + CSS
<p class="warning">Hi</p>
/* CSS */ .warning { color:red; font-size:24px; }
❌ Inline event handlers
<button onclick="doThing()">Click</button>
✅ Add listeners in JS
<button id="doBtn">Click</button>
<script>
document.getElementById('doBtn').addEventListener('click', () => alert('Done!'));
</script>
Part 8 — Practice (Do First, Then Peek)
- Add a nav with two links and ensure it remains readable with JS disabled.
- Style the page title using CSS only (no inline styles).
- Implement a button that reveals a hidden paragraph via JS (use the
hidden
attribute). - Break something on purpose (remove the stylesheet link) and use DevTools to diagnose.
One possible solution
<nav><a href="#">Home</a> · <a href="#">Docs</a></nav>
<h1 class="title">Layered Page</h1>
<p id="extra" hidden>Surprise content!</p>
<button id="revealBtn">Reveal</button>
/* CSS */
.title { color:#00ffff; font-weight:800; }
#extra[hidden]{ display:none; }
// JS
const extra = document.getElementById('extra');
document.getElementById('revealBtn').addEventListener('click', () => {
extra.hidden = !extra.hidden;
});
Part 9 — Mini-Project: Profile Card (Layered)
Create a profile card that looks good with CSS and has a small JS interaction (toggle extra details).
Starter HTML
<section class="card profile">
<h2>Your Name</h2>
<p>Short bio line here.</p>
<button id="moreBtn">Show more</button>
<div id="more" hidden>
<p>More details about you...</p>
</div>
</section>
CSS & JS sketch
.profile { max-width: 420px; margin: 1rem auto; background:#1c1c1c; padding:1rem; border-radius:12px; border:1px solid #2a2a2a; }
#more[hidden]{ display:none; }
const more = document.getElementById('more');
document.getElementById('moreBtn').addEventListener('click', () => {
more.hidden = !more.hidden;
});
Lesson 2 Dictionary
- addEventListener
- JavaScript method to attach event handlers without inline attributes (e.g.,
elem.addEventListener('click', fn)
). - <article>
- Self-contained composition (e.g., a card or post) within the main content.
- <button>
- Interactive control; pair with JS listeners for behavior.
- class (attribute)
- Token(s) used as styling/JS hooks (e.g.,
class="warning"
). - CSS
- Presentation layer: layout, spacing, color, typography, responsiveness.
- CSSOM
- Tree of parsed CSS rules; combined with the DOM to create the render tree.
- defer (script attribute)
- Downloads a script during parsing and executes after the document is parsed.
- <details>
- Native disclosure widget used for hints/solutions in this lesson.
- DOM
- Document Object Model — parsed HTML as a live tree that JS can read/modify.
- Event listener
- Function responding to user/system events (click, input, etc.).
- <header>
- Introductory content/navigation for a page or section.
- hidden (attribute)
- Boolean attribute to hide content (e.g., toggle with JS by flipping
elem.hidden
). - id (attribute)
- Unique identifier for an element (
id="themeBtn"
), used to select it in JS or link targets. - Inline styles
- CSS placed directly on elements via
style="..."
; discouraged in favor of classes. - JavaScript
- Behavior layer: events, state, DOM/CSSOM updates, storage, fetch.
- <link>
- References a stylesheet:
<link rel="stylesheet" href="styles.css">
. - localStorage
- Key–value storage for persisting simple preferences (e.g., theme).
- <main>
- Primary content landmark of the page.
- <nav>
- Section containing navigation links.
- <p>
- Paragraph of text.
- Render Tree
- Combination of DOM + CSSOM used by the browser to layout and paint.
- rel (attribute)
- Relationship of a linked resource (e.g.,
rel="stylesheet"
on<link>
). - <script>
- Loads and executes JavaScript. Use
defer
for non-blocking behavior. - Separation of Concerns
- Keeping structure (HTML), presentation (CSS), and behavior (JS) in distinct layers.
- src (attribute)
- Specifies the external resource for scripts (and other elements), e.g.,
src="app.js"
. - <summary>
- Label for a
<details>
disclosure widget.