Table of Contents
- Module 1: Getting Started with Web Development
- Module 2: Building Your First Web Page
- Module 3: Adding Style with CSS
- Module 4: Working with CSS Layout and Responsiveness
- Module 5: Introduction to JavaScript
- Strings
- Numbers
- Variables
- Operators
- Control Flow: Blocks and Scope
- Control Flow: if/else Conditionals
- Function Declarations
- Function Arguments
- Function Return Values
- Functions with Multiple Arguments
- Object Literals
- Arrays
- while Loops
- for Loops
- The forEach Loop
- Debugging JavaScript in the Browser
- Selecting Elements
- Working with Element Attributes
- The classList API
- The Script Tag
- The Window Object
- Module 6: Building Bethany’s Pie Shop
- Project Setup and HTML Boilerplate
- Building the Header and Navigation
- Building the Footer Markup
- The Aside and Logo Column
- The Responsive Picture Element
- Title Text and the Order Calculator Table
- Linking the CSS File
- CSS Custom Properties (Variables)
- Resetting Browser Defaults
- Styling the Navigation
- Styling the Heading
- Styling the Order Calculator
- Styling the Footer
- Building the Responsive Layout with Flexbox
- Linking the Script File
- Modeling the Inventory Object
- The Sum Function
- The Calculate Function
- Listening for DOMContentLoaded
- Wiring Up Box Event Listeners
- Debugging the Calculator Step by Step
- Summary
This course teaches web development from a completely blank page all the way up to building a small, real, interactive website. Over the course of the material, you will build up an understanding of how a web page is structured with HTML, given a look and feel with CSS, and made interactive with JavaScript. The major topics covered include the meaning and purpose of HTML elements, how a page gets its appearance from style sheets, the basics of computer programming, and how to write code that makes a page interactive.
By the end of the course, you will have built a small responsive website — Bethany’s Pie Shop — from scratch, including a working order-total calculator, and you should feel ready to move on to front-end frameworks such as React, Angular, or Vue; back-end technologies such as Node.js; and more advanced CSS techniques.
mindmap
root((Web Page))
HTML
Structure
Elements and tags
Semantic meaning
CSS
Style and look and feel
Selectors and rulesets
Layout (Box model, Flexbox)
Responsiveness (media queries)
JavaScript
Behavior and interactivity
Variables, functions, objects
DOM manipulation
Events
Module 1: Getting Started with Web Development
The Final Project: Bethany’s Pie Shop
Throughout this course, you build a website from the ground up for a fictional client, Bethany, who runs a local business selling pastries, pies, and other baked treats. She wants to take her pie shop online, and the finished website acts as the running example for every concept introduced.
The finished home page welcomes customers to the store, reflects Bethany’s brand identity, and includes a bit of her story. Further down the page there is an order calculator where customers can get an order total for the pies they want to buy just by typing quantities into input boxes.
The site is also built to be responsive: it looks good on both desktop and mobile devices. Opening the browser’s developer tools and switching to a mobile device emulation view reveals a different layout — the icon logo disappears and the hero picture is cropped differently — all achieved by building the page to adapt to both mobile and desktop contexts.
The topics needed to build this page, in order, are:
- HTML — the structure of the content.
- CSS — the styling, appearance, and layout, including responsive behavior.
- JavaScript — the interactivity (the order calculator logic).
Writing Your First HTML Element
To get a feel for HTML immediately, you can open a browser and go to an online HTML/CSS/JS sandbox (such as codepen.io/pen) and expand the HTML pane. Typing the following is your first HTML element:
<h1></h1>
The h stands for header, meaning this is the primary, or first, header on the page — essentially the title of the page. Adding text between the opening and closing tags produces a complete element:
<h1>Hello World</h1>
“Hello World” is a traditional placeholder message used since an old programming book from the 1970s to demonstrate the simplest possible program in a given language — you will see it used often as you continue learning to program.
This tiny example is not yet a full web page, but it demonstrates the core idea: web pages are made up of elements that, when stacked together in just the right way, form the foundation of the web.
Setting Up Your Code Editor
Many code editors are available, most of them free with rich features and large communities. Visual Studio Code (code.visualstudio.com) is the recommended editor for this course. It is free, cross-platform (Windows, macOS, Linux), and supports extensions — additional functionality that anyone can build and share.
A particularly useful extension for front-end development is Live Server, which serves your project’s files to the browser the same way a real web server would — without you needing to write any server-side code — and automatically refreshes the browser whenever you save a change.
Steps to set it up:
- Install VS Code from
code.visualstudio.com. - Open the Extensions view (the icon that looks like a box being snapped into another box) from the left icon bar.
- Search for Live Server and click Install.
- Reload the VS Code window (
Ctrl+Shift+P/Cmd+Shift+P, then type “Reload Window” and press Enter). - A Go Live button appears in the bottom status bar. Clicking it launches the current page in the browser through the extension.
VS Code also ships with Emmet, a set of shortcuts that speed up writing HTML. For example, typing html:5 and pressing Tab scaffolds a complete HTML5 document stub. Typing Hello World inside the <body> and saving, then right-clicking the file and choosing Open with Live Server, launches the page in the browser. From that point on, every time you save the file, Live Server automatically updates the browser with the latest changes.
Getting the Sample Project Files
The course’s example code is hosted on GitHub using two branches:
| Branch | Purpose |
|---|---|
main | The finished version of the site — what your code should resemble once the course is complete. |
start | The starting files you need to begin the course, without the code you are about to write. |
To obtain the starter files without needing deep GitHub knowledge, you can download a zip archive containing them, extract it, and open the resulting folder in Visual Studio Code (File > Open Folder…). The starter folder already contains the images for the website as well as placeholder HTML, script, and style files that you build up throughout the course.
Module 2: Building Your First Web Page
What Is HTML?
HTML stands for HyperText Markup Language. Breaking that down piece by piece:
- HyperText refers to the fact that documents on the web are linked together through hyperlinks. Ignoring the “hyper” prefix for a moment, this means text documents are connected together by links — the foundation of the web. The prefix “hyper” is used in the sense of making something bigger (as in “hyperspace”), not in the sense of “hyperactive.” As you navigate from one site to the next, they are all tied together through a web of links, which is where the term “the web” comes from.
- Markup Language refers to the code you write to create these documents and establish the links between them.
The building blocks of a markup language are called HTML elements.
What Is an HTML Element?
An element is a building block of a web page — much like a single brick is a building block of a structure built from many bricks. HTML elements are combined together to build the structure of an entire web page.
You can inspect the underlying HTML of any page in a couple of ways:
- Right-click → View Page Source shows the raw HTML of the page. This works fine for a very small page, but on a real, large website the output becomes difficult to read.
- Right-click → Inspect opens the Elements pane of the browser’s developer tools. This is a much better way to explore markup on real-world pages: elements are collapsible, hovering over an element highlights it on the rendered page, and you can navigate the structure of even a very large page.
So, every HTML page is made up of a series of HTML elements that build on one another, just like a series of bricks set together just right make up a house.
Elements: Tags and Attributes
Each HTML element is made up of at least two parts:
- Tag — identifies what the element is and the role it plays on the page (for example
html,head,body,h1,p,img). - Attribute(s) — additional information associated with the element. For example, an
imgelement hassrcandtitleattributes.
An analogy: if you described yourself to someone, you might say “I’m a person” (the element’s tag), and then add details like “my name is Craig, I have brown eyes and brown hair” (the element’s attributes). In the same way, an image element has the img tag, and its src and title values are its attributes. The src attribute tells the browser where to find an image file on the server; the title attribute gives a title, or brief description, of the element.
So, HTML pages are made up of a series of building-block elements. Each element is identified by its tag, and each element can have zero, one, or many attributes. Two attributes you will see repeatedly throughout this course are:
| Attribute | Purpose |
|---|---|
id | Stands for identifier — gives a unique name to a single element on the page. |
class | Identifies an element and can also be used to change how it looks via a style sheet. A class can be applied to many elements at once. |
A Tour of Common HTML Elements
The MDN Web Docs HTML elements reference is the definitive place to learn about every available HTML element and web API. Broadly, elements are grouped into categories such as:
| Category | Examples / Purpose |
|---|---|
| Document structure | html, head, body |
| Content sectioning | Elements that divide a page into logical regions |
| Text content | Elements for blocks of text |
| Inline text semantics | Elements describing meaning within a run of text |
| Image and multimedia | Pictures, audio, video |
| Embedded content | Embedding external resources |
| Scripting | Elements that host or reference JavaScript |
| Demarcating edits | Elements that show insertions/deletions |
| Table content | Elements for tabular data |
| Forms | Elements for collecting user data |
| Interactive elements | Elements users can directly interact with |
| Web components | Elements enabling custom, reusable components |
| Obsolete/deprecated | Elements no longer supported on the web |
You do not need to memorize every element up front — the best approach is to spend time becoming familiar with elements and learning what kind of content they are most appropriate for, one element at a time.
Using Common Elements with Emmet
Emmet is a tool built into Visual Studio Code that speeds up writing HTML through abbreviations: type a short string of characters and press Tab to expand it into full markup.
Building a basic page with Emmet:
<!-- typing "html:5" + Tab scaffolds this stub -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
</body>
</html>
Inside <body>, typing h1 + Tab creates an <h1> element with the cursor placed between the tags, ready for a page title:
<h1>My Page Title</h1>
Adding a paragraph (p + Tab):
<p>Some introductory paragraph text.</p>
Adding a link (a + Tab) automatically includes the href attribute, which stands for hyperlink reference and points the link to a web address:
<a href="https://www.example.com">Visit our site</a>
Adding a table with Emmet’s table abbreviation scaffolds the root <table> element, and further abbreviations build out the header and body:
<table>
<thead>
<tr>
<th>Name</th>
<th>Twitter</th>
</tr>
</thead>
<tbody>
<tr>
<td>Craig Shoemaker</td>
<td>@craigshoemaker</td>
</tr>
<tr>
<td>John Papa</td>
<td>@john_papa</td>
</tr>
</tbody>
</table>
Rendered in the browser, this produces a very plain-looking page: a title, a paragraph, and an unstyled table. The structure — the HTML — is complete; styling comes next.
The Document Object Model (DOM)
The browser represents a loaded web page in memory through the Document Object Model (DOM). Every web page is a single document inside the browser, and that document is represented by an in-memory object simply called document.
Opening the developer tools’ Console tab and typing document and pressing Enter returns a reference to the in-memory document. Expanding it reveals essentially everything visible in the Elements tab — the entire HTML structure of the page is available through document.
An object is best understood as an approximation of something in the real world. Just as thinking of “a house” conjures walls, a roof, windows, and a door — while glossing over the plumbing, wiring, and paint — programming objects are abstractions: placeholders for things whose full detail is not the immediate concern. In the browser, each element on the page is represented by an object, and together these objects form the DOM.
The word Model in DOM alludes to the fact that it is a representation of a structure, similar to how a model plane represents a real plane. The browser uses this tree of objects (the DOM) to understand and interface with the page.
flowchart TD
A[document] --> B[html]
B --> C[head]
B --> D[body]
C --> C1[title]
C --> C2[meta tags]
D --> D1[h1]
D --> D2[p]
D --> D3[table]
D3 --> D3a[thead]
D3 --> D3b[tbody]
A useful DevTools shortcut: whenever you select an element in the Elements tab, the browser creates a temporary reference to it accessible in the console as $0. Selecting a different element and typing $0 again returns a reference to the newly selected element — a quick way to inspect whatever you have currently selected in the Elements panel.
How Search Engines Read Markup
Search engines interpret a web page differently than a human looking at it in the browser. The key concept here is semantic markup — not a special set of HTML elements, but rather how you use elements together to convey the relative significance of content.
Consider this non-semantic fragment:
Introduction
Once upon a time there was a beautiful princess who lived happily ever after.
As far as the HTML is concerned, there is no way to tell that “Introduction” is a title with different significance than the following text — both are just plain text with no distinguishing markup.
Rewriting it with semantic elements:
<h1>Introduction</h1>
<p>Once upon a time there was a beautiful princess who lived happily ever after.</p>
Now a computer can tell that <h1> is a page header — not just any title, but the primary and most important one on the page. Search engines use many criteria to determine rankings, but one clear signal is which tag wraps a piece of content:
| Element | Signal sent to a search engine |
|---|---|
<h1> | Primary title of the page — most important content |
<h2> | Secondary heading — still important, but subordinate to <h1> |
<main> | The main, most important content of the page |
<aside> | Extra/supplementary content, not core to the page’s message |
Writing semantic markup means being familiar with the intended purpose of each tag and using tags according to their documented meaning, rather than arbitrarily.
Validating Your HTML
To check whether HTML follows the specification correctly, you can submit it to a validation service. The W3C (the organization responsible for the HTML specification) provides a validator at validator.w3.org.
You can validate code by supplying a URL, uploading a file, or pasting code directly (“direct input”). After clicking Check, the validator reports issues — for example, a missing lang attribute on the <html> element, or a missing <!DOCTYPE html> declaration at the very start of the page.
As you write more HTML, periodically validating it helps ensure your markup follows the rules of the specification correctly.
Anatomy of a Web Page: HTML, CSS, and JavaScript
A finished web page — the user interface — is the product of three technologies working together. An analogy to a person helps clarify the roles:
| Aspect of a person | Web technology | Role |
|---|---|---|
| Skeletal system (bone structure) | HTML | Structure of the page/content |
| Clothes (physical appearance) | CSS (Cascading Style Sheets) | Style, look, and feel |
| Brain (thought, logic, reasoning) | JavaScript | Interactivity, programming logic, business rules |
flowchart LR
HTML["HTML — Structure<br/>(skeleton)"] --> Page[Rendered Web Page]
CSS["CSS — Style<br/>(clothes)"] --> Page
JS["JavaScript — Behavior<br/>(brain)"] --> Page
HTML alone (as seen in the earlier bare examples) is not much to look at — just information thrown on the page. CSS provides the style and layout. JavaScript brings the interactivity. Together, the three form a fully functioning website.
Module 3: Adding Style with CSS
CSS Style Basics
Cascading Style Sheets (CSS) let you describe how you want elements to appear on the page — fonts, colors, sizes, layouts, and much more. Style rules can be added in a few different locations (covered later in this module). A simple starting example, adding a <style> block to the <head> of a page:
<head>
<style>
div {
background-color: #333;
color: #fff;
}
</style>
</head>
<body>
<div>Hello</div>
</body>
Each style rule is formatted as a property name, a colon, and a value. CSS accepts color information in a few different formats; the example above uses a hex code. Visual Studio Code shows a small color preview/picker when you hover over a recognized color value, and lets you pick any color visually.
Anatomy of a CSS Ruleset
A complete CSS rule, taken as a whole, is known as a ruleset. Consider the following example:
.container p:first-child::first-letter {
font-size: 2em;
}
| Part | Example | Meaning |
|---|---|---|
| Selector | .container p | Locates the element(s) the browser should target |
| Pseudo-class | :first-child | Selects an element based on state/relationship — here, the very first child |
| Pseudo-element | ::first-letter | Selects a specific character/portion of an element — here, its first letter |
| Declaration block | { ... } | The curly braces bound the rules applied to the matched elements |
| Declaration (property + value) | font-size: 2em; | A single rule; made of a property and a value (or keyword) |
In plain English, the selector above says: “Find an element with the class container, locate the paragraph inside it, find its first child element, and apply the style only to the very first letter of that element.”
Cascading Rules
CSS is cascading — multiple rules can apply to the same element, and specific precedence rules decide which one wins. The simplest case: when two rules with equal specificity target the same selector, the rule that appears last in the source wins.
h1 {
color: blue;
}
h1 {
color: gray; /* this one wins — it comes later */
}
Specificity
Specificity determines which rule wins when rules of different selector types conflict. A class-based rule is more specific than an element (tag) rule and therefore wins, regardless of source order:
.heading {
color: blue; /* wins, because .heading is more specific than h1 */
}
h1 {
color: gray;
}
<h1 class="heading">Title</h1>
The reasoning: an element’s tag broadly describes what it is, but a class adds more specific information about that particular instance. A general rule of thumb ordering, from least to most specific: tag selector < class selector < ID selector < inline style attribute.
Inheritance
Some CSS properties are inherited by child elements from their parent; others are not.
body {
background-color: lightgray; /* inherited by children unless overridden */
}
h1 {
background-color: white; /* overrides the inherited value for h1 only */
}
Dimensional properties such as width are not inherited:
body {
background-color: lightgray;
}
main {
background-color: gray;
width: 50%;
}
div {
background-color: darkgray; /* takes up 100% of its container (main), not 50% */
}
Since width does not inherit, the div fills 100% of its parent main element unless it is given its own explicit width. Inheritance is powerful because it saves you from restating the same rules over and over for every nested element.
Where to Place Style Rules
CSS rules can be placed in three different locations, and where you place them affects how they cascade:
| Location | Example | Notes |
|---|---|---|
Inline style attribute | <h1 style="color: blue;"> | Highest precedence; least flexible — generally discouraged except in special cases |
<style> element in <head> | <style>h1 { color: blue; }</style> | Applies to the current document only |
External stylesheet file (.css) | <link rel="stylesheet" href="styles.css"> | Recommended: enables browser caching and easier maintenance across multiple pages |
Linking an external stylesheet:
<head>
<link rel="stylesheet" href="styles.css">
</head>
/* styles.css */
h1 {
color: blue;
}
Using an external CSS file is important for caching: the browser downloads the stylesheet once and each page in the site can reference the same cached file, making the site faster overall, while also making the styles easier to maintain.
If styles for the same selector and property exist in more than one of these locations at once, the cascade resolves as follows (from lowest to highest precedence):
flowchart LR
A["External stylesheet<br/>(styles.css)"] --> B["Style element<br/>(<style> in <head>)"]
B --> C["Inline style attribute<br/>(style="...")"]
C --> D[Wins: most specific in scope]
Introduction to Selectors
Selectors locate elements on the page so CSS knows which ones to style. The main categories are tag, class, ID, and attribute selectors. Example markup used to demonstrate each type:
<h1>Page Title</h1>
<p>Some intro text.</p>
<div id="tutorial-1" class="tutorial">
<h2>Subheading 1</h2>
<img src="https://via.placeholder.com/150">
<p>Label text</p>
</div>
<div id="tutorial-2" class="tutorial">
<h2>Subheading 2</h2>
<img src="https://via.placeholder.com/150">
<p>Label text</p>
</div>
Both divs share the class tutorial (so they can be styled as a set) but each has its own unique id (so either can be targeted individually).
Tag Selectors
A tag selector targets every element with a given tag name:
img {
border: 1px solid gray;
}
This applies the border to every <img> element on the page.
Class Selectors
A class selector begins with a dot (.) and targets every element carrying that class name:
.tutorial {
border: 1px solid black;
}
Class selectors are valuable because the same class (label) can be applied to many different elements, letting you style a whole category of elements — e.g., “all tutorial containers” — consistently.
ID Selectors
An ID selector begins with a hash/pound sign (#) and targets the single element carrying that unique id:
#tutorial-1 {
background-color: #eee;
}
You typically use id to uniquely identify one specific element, such as a container that needs distinct styling or an element that collects data from the user.
Attribute Selectors
An attribute selector targets elements based on the presence or value of an attribute, using square brackets:
img[src="https://via.placeholder.com/150"] {
border: 3px solid blue;
}
This selects only <img> elements whose src attribute equals the given value — giving very precise targeting when needed.
Multiple (List) Selectors
A list selector applies the same rule to more than one selector at once by separating them with commas:
h1, h2 {
color: darkslateblue;
}
.tutorial, h1 {
border: 1px solid black;
}
The second example mixes a class selector and a tag selector in the same list, styling both the tutorial containers and the top-level heading with a border.
Combinators
A child combinator (>) targets elements that are direct children of another selected element:
.tutorial > p {
font-size: 1.2em;
}
This targets only <p> elements that are direct children of an element with the tutorial class — not paragraphs nested more deeply, and not paragraphs outside a .tutorial container.
Building Styles in Real Time with DevTools
Rather than guessing at a selector, saving a file, and checking the browser repeatedly, the browser’s own developer tools let you experiment live: right-click an element and choose Inspect, then use the Styles panel next to the Elements tab to add and toggle CSS rules and see the effect immediately, without editing any file.
A typical experimental sequence turning a plain <div> into a styled button:
.button {
border: 1px solid #333;
padding: 10px 16px;
background-color: #ccc;
width: 40px;
text-align: center;
font-family: Arial, Helvetica, sans-serif;
cursor: pointer;
}
Once the rule looks right in the live Styles panel, you copy it back into your actual stylesheet (creating a .button class rule) and apply the class to the element in HTML — that change persists across page reloads, whereas edits made only in DevTools are lost on refresh.
Module 4: Working with CSS Layout and Responsiveness
The Box Model and Box Sizing
Every element on a page can be thought of as a rectangle — a box. When several boxes sit next to each other, the browser must decide how to calculate the space each one occupies. Key terms:
| Term | Meaning |
|---|---|
| Width / Height | The element’s own content dimensions |
| Border | The visible outline drawn around the box; can vary in thickness |
| Margin | Invisible space outside the border, keeping other elements away from this box |
| Padding | Invisible space inside the border, between the border and the box’s content |
flowchart TD
subgraph Margin[Margin]
direction TB
subgraph Border[Border]
direction TB
subgraph Padding[Padding]
direction TB
Content[Content: width x height]
end
end
end
Given an element declared with width: 150px, a 10px border, and 10px of padding, there are two possible interpretations of “how wide is this box, really?”:
| Box model | Rendered width calculation | Total rendered width |
|---|---|---|
| Classic (content-box) | width + border + padding are added on top | 150 + 10 + 10 + 10 + 10 = 170px |
border-box | Border and padding are rendered inside the declared width | 150px (border/padding eat into the content area) |
Both models are supported by browsers; box-sizing: border-box is generally the recommended approach because the math stays predictable while designing a layout. A common pattern applies it globally:
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
With this in place, every element (and its ::before/::after pseudo-elements) inherits the border-box sizing strategy, so declared widths and heights already account for border and padding.
Building Layouts with Flexbox
Flexbox is one of the easiest ways to build multi-column layouts. Starting from a container with a class and two child columns:
<div class="columns">
<div>Column 1</div>
<div>Column 2</div>
</div>
.columns {
border: 1px solid #333;
width: 100%;
display: flex;
}
.columns > div {
border: 1px solid #999;
flex: 1;
}
Setting display: flex on the container immediately arranges the child divs side by side. Setting flex: 1 on each child tells the browser to evenly distribute all available space among the children, giving each one an equal share (one “portion” of the total space). Adding a third <div> automatically results in three equal-width columns, because the available space is now split three ways.
To give one column more room than the others, target it specifically using the :nth-of-type pseudo-class and give it a larger flex value:
.columns > div:nth-of-type(2) {
flex: 2;
}
This makes the second column take up two proportional units of space, while the first and third columns each take one unit — so, out of 4 total units, column 2 gets half the available width and columns 1 and 3 split the rest.
| Flex property | Purpose |
|---|---|
display: flex | Turns on Flexbox layout for the container’s direct children |
flex: <n> | Shorthand controlling how much proportional space a flex item claims relative to its siblings |
flex-direction | Controls the main axis direction (e.g. row, row-reverse) |
flex-basis | The starting size of a flex item before growing/shrinking |
flex-grow | How much an item grows to fill extra space |
flex-shrink | How much an item shrinks when space is constrained |
The Responsive Viewport
Before writing responsive CSS rules, a special metadata tag is required so mobile devices interpret the page’s width correctly:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
width=device-widthtells the browser to render the viewport at the actual width of the device, rather than a desktop-sized default.initial-scale=1.0sets the initial zoom level to 100% — neither zoomed in nor zoomed out.
Without this tag, mobile browsers commonly render the page as if it were a wide desktop page and then shrink it to fit, defeating the purpose of a responsive layout.
Responsive Design with Media Queries
Media queries let you target CSS rules at specific device types or viewport sizes, enabling one style sheet to serve very different layouts to desktop and mobile.
Best practice is to design mobile-first: write the base styles for small screens, then use a media query to add or override rules for larger screens. Given three placeholder blocks intended for different contexts:
<div class="mobile">Mobile content</div>
<div class="desktop">Desktop content</div>
<div class="print">Print-only content</div>
Hide the desktop- and print-only elements by default (mobile-first baseline):
.desktop, .print {
display: none;
}
A print media query reverses the visibility for printed output:
@media print {
.mobile, .desktop {
display: none;
}
.print {
display: block;
}
}
A screen media query targets the desktop viewport, activating once the browser is at least 768px wide:
@media screen and (min-width: 768px) {
.mobile {
display: none;
}
.desktop {
display: block;
}
}
Below 768px the mobile elements show; at 768px and above, the desktop elements take over instead. The browser’s developer tools include a device toolbar (toggle-able from an icon in DevTools) that simulates a range of real devices, orientations, and zoom levels, which is far more reliable for testing media queries than manually resizing the browser window.
flowchart TD
A[Viewport width] -->|"< 768px"| B[Mobile styles: .mobile visible]
A -->|">= 768px (screen)"| C[Desktop styles: .desktop visible]
A -->|"@media print"| D[Print styles: .print visible]
Checking Browser Support
To confirm whether a given CSS feature is safe to use across browsers, the reference site caniuse.com shows a compatibility table for the feature, developer notes, and a “Known issues” section covering edge cases.
| CSS feature example | Typical support level |
|---|---|
| Flexbox | Broad support across virtually all modern browsers |
| Hanging punctuation | Poor support — not safe to rely on in production |
Most modern layout techniques used in this course (Flexbox, media queries, custom properties) are well supported, but it is good practice to check caniuse.com whenever you are unsure about a newer or more obscure CSS feature.
Module 5: Introduction to JavaScript
JavaScript is the language of the web: it runs in the browser (and can also run on servers). This module focuses on the code you write to make a web page interactive in the browser. A computer’s processor really only understands 1s and 0s; programming languages like JavaScript exist to translate human-readable instructions into something the machine can execute. An interpreter is the program that reads JavaScript line by line and carries out the instructions.
All of the experiments below can be tried directly in a browser’s Console tab (open with about:blank in the address bar, then open DevTools). Ctrl/Cmd+L clears the console.
Strings
Typing a bare word like hello into the console produces a ReferenceError: hello is not defined, because the interpreter thinks you are referring to a variable that does not exist. Wrapping the value in quotation marks tells the interpreter it is a string — a series of characters:
"hello"
// "hello"
"hello".length
// 5
Strings represent text, including whitespace such as spaces and line breaks, strung together as a single value.
Numbers
The console also understands numbers natively, and can evaluate arithmetic expressions directly:
1 + 1
// 2
6 * 6 / (2 * 3)
// 6
An expression is a line of code that, when evaluated, produces a result. This concept extends far beyond arithmetic — expressions appear throughout JavaScript.
Variables
A variable is a container whose contents can change. Declaring one with let:
let name = "Craig";
name; // "Craig"
name = "John";
name; // "John"
A constant, declared with const, can be assigned a value once and never reassigned:
const maxQuantity = 5;
maxQuantity = 3; // TypeError: Assignment to constant variable.
| Keyword | Reassignable? | Typical use |
|---|---|---|
let | Yes | Values expected to change over time |
const | No | Values meant to remain fixed once set (placeholders for constants) |
Operators
The + operator performs arithmetic on numbers, but on strings it performs concatenation — sticking two strings together:
"Craig" + "Shoemaker"
// "CraigShoemaker"
"Craig " + "Shoemaker"
// "Craig Shoemaker"
A single equals sign (=) is the assignment operator, not a test for equality — it is what you use when declaring or updating a variable (let x = 1;). Trying to write 1 + 1 = 2 produces an error, because the interpreter attempts (and fails) to assign a value to the expression 1 + 1.
To test equality, JavaScript uses == or (preferably) ===, which returns a Boolean value (true/false):
1 + 1 == 2 // true
1 + 2 == 2 // false
| Operator | Meaning |
|---|---|
+ | Addition (numbers) or concatenation (strings) |
= | Assignment |
== | Loose equality (performs type coercion) |
=== | Strict equality (recommended default) |
Control Flow: Blocks and Scope
Control flow describes how a program executes — and sometimes skips — lines of code, giving the illusion of “decision making.” The basic building block of control flow is a block, delimited by curly braces { }.
let name = "Craig";
console.log(name); // Craig
Declaring the same variable name twice with let in the same scope produces an error (Identifier 'name' has already been declared). However, let gives variables block scope — wrapping a second declaration in its own { } block isolates it from the outer scope:
let name = "Craig";
{
let name = "Tyler"; // valid: separate block scope
}
console.log(name); // Craig — the outer variable is unaffected
Control Flow: if/else Conditionals
An if statement (conditional) lets a program make decisions based on whether an expression evaluates to true or false:
let name = "Craig";
if (name === "Craig") {
console.log("Hi Craig!");
} else if (name === "Tyler") {
console.log("Tyler, is that you?");
} else {
console.log("Wait, where's Craig?");
}
Prefer the strict equality operator (===) inside conditionals over ==, since == performs type coercion with sometimes surprising results. if/else if/else chains let you test multiple conditions in sequence, falling through to a final default else branch when none of the earlier conditions match.
flowchart TD
Start([Evaluate name]) --> Q1{name === "Craig"?}
Q1 -- true --> A["console.log('Hi Craig!')"]
Q1 -- false --> Q2{name === "Tyler"?}
Q2 -- true --> B["console.log('Tyler, is that you?')"]
Q2 -- false --> C["console.log(\"Wait, where's Craig?\")"]
Function Declarations
Repeating the same block of logic in multiple places creates a maintenance problem: any bug fix or change has to be made in every copy. Wrapping reusable logic in a function solves this:
function greet() {
let name = "Craig";
if (name === "Craig") {
console.log("Hi Craig!");
} else {
console.log("Wait, where's Craig?");
}
}
greet(); // Hi Craig!
greet(); // Hi Craig! (can be called repeatedly)
Declaring a function only defines it — nothing runs until the function is called using its name followed by parentheses (greet();). Convention indents nested blocks of code (the function body, and any blocks nested further inside it, such as if blocks) for readability.
Function Arguments
A function becomes flexible once you can pass data into it via arguments, instead of relying on hard-coded values inside the function body:
function greet(name) {
if (name === "Craig") {
console.log("Hi Craig!");
} else if (name === "Tyler") {
console.log("Tyler, is that you?");
} else {
console.log("Wait, where's Craig?");
}
}
greet("Craig"); // Hi Craig!
greet("Tyler"); // Tyler, is that you?
greet("Jacob"); // Wait, where's Craig?
Callers can also pass in variables rather than hard-coded literals:
let name = "Craig";
greet(name); // Hi Craig!
Function Return Values
Data can also come out of a function using the return keyword. Whatever follows return becomes the function’s result when it is called:
function greet(name) {
let result = "";
if (name === "Craig") {
result = "Hi Craig!";
} else if (name === "Tyler") {
result = "Tyler, is that you?";
} else {
result = "Wait, where's Craig?";
}
return result;
}
let message = greet("Craig");
console.log(message); // Hi Craig!
Declaring the result variable at the top of the function with a sensible default (here, an empty string), then returning it as the final statement, is a common and readable pattern. Returning a value (rather than only calling console.log inside the function) means the same greet logic can be reused anywhere — printed to the console, displayed on the page, or passed to other functions — a principle known as separation of concerns: the function is only concerned with producing a greeting, not with what happens to that greeting afterward.
Functions with Multiple Arguments
Functions can accept zero, one, or many arguments:
function add(number1, number2) {
return number1 + number2;
}
let sum = add(2, 2);
console.log(sum); // 4
Object Literals
An object literal groups related data (and, optionally, behavior/functions) together into a single value, rather than tracking several loose variables:
let firstName = "Craig";
let lastName = "Shoemaker";
let twitter = "@craigshoemaker";
// grouped as an object literal:
let person = {
firstName: "Craig",
lastName: "Shoemaker",
twitter: "@craigshoemaker",
greet: function () {
console.log("My name is " + this.firstName);
}
};
console.log(person); // logs the whole object
console.log(person.firstName); // "Craig" — dot notation accesses a property
person.greet(); // "My name is Craig"
Inside an object’s own function, the this keyword refers to the object itself, giving access to its other properties (such as this.firstName above). Object properties are separated by commas, not semicolons, since they are part of one larger declaration rather than separate statements.
Arrays
An array represents an ordered list of values, declared with square brackets:
let numbers = [100, true, "JavaScript"];
console.log(numbers); // [100, true, "JavaScript"]
console.log(numbers[0]); // 100 — arrays are zero-indexed
console.log(numbers.length); // 3
numbers.push(42); // adds an item, returns the new length
Arrays can mix data types (numbers, Booleans, strings, and more) freely within the same list. Each item’s position is called its index, and arrays are zero-indexed — the first item is at index 0.
| Array capability | Example |
|---|---|
| Access by index | numbers[0] |
| Length | numbers.length |
| Add an item | numbers.push(value) |
| Iterate | for, while, or forEach (see below) |
while Loops
A loop repeats a block of code — it lets a program iterate. A while loop repeats its body while its test expression remains true:
let count = 0;
const max = 10;
while (count < max) {
console.log(count);
count = count + 1;
}
// logs 0 through 9
Because count starts at 0 and the loop stops once count is no longer less than max, the loop body runs exactly 10 times, printing 0 through 9 (not 1 through 10) — the same zero-indexed reasoning seen with arrays. Forgetting to increment the counter would create an infinite loop.
for Loops
A for loop packs the initializer, the test condition, and the increment step into a single, more compact line:
for (let i = 0; i < 10; i++) {
console.log(i);
}
// logs 0 through 9 — identical result to the while loop above
i++ is shorthand for i = i + 1. The conventional counter name i stands for index. A very common pattern combines a for loop with an array’s length to visit every item:
let numbers = [1, 2, 3];
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
// logs 1, 2, 3
The forEach Loop
Arrays provide a built-in forEach method that iterates over every item without manually managing an index variable:
let days = ["Monday", "Tuesday", "Wednesday"];
days.forEach((day, index) => {
console.log(`day: ${day}, index: ${index}`);
});
// day: Monday, index: 0
// day: Tuesday, index: 1
// day: Wednesday, index: 2
forEach passes the current item (day) and its position (index) into an anonymous function — a function with no name, defined inline as the argument to forEach. The example above also introduces template strings: text wrapped in back ticks (`) that can embed variables directly using ${expression} syntax, instead of concatenating with +.
| Loop type | Best suited for |
|---|---|
while | Repeating while a condition holds, when the number of iterations is not known up front |
for | Repeating a known/counted number of times; classic indexed iteration |
forEach | Iterating every item of an array with clean, minimal syntax |
Debugging JavaScript in the Browser
The browser’s debugger lets you pause code execution at a specific line and inspect what is happening at that moment — one of the most valuable tools available in the developer tools.
Given a small script with a bug (referencing an undeclared variable firstName instead of first):
let first = "Craig";
let last = "Shoemaker";
console.log(firstName + " " + last); // bug: should be "first"
Steps to debug in the browser:
- Open DevTools and switch to the Sources tab.
- Click a line number to set a breakpoint — a point where execution pauses.
- Reload the page; execution stops at the breakpoint, and you can hover over variables to inspect their current values.
- Use the stepping controls to advance execution.
| Debugger control | Effect |
|---|---|
| Resume script execution | Continues running until the next breakpoint (or the end) |
| Step over next function call | Executes the current line and moves to the next, without entering called functions |
| Step into function call | Enters the function being called on the current line |
| Step out of function call | Finishes the current function and returns to its caller |
Once the mistaken variable name is spotted (firstName should be first), the fix can be made in the editor while still paused, and re-running confirms the corrected value.
Selecting Elements
To manipulate a page from JavaScript, you first need to select the element(s) you want to work with. Given two card elements on a page:
<section id="craig-shoemaker" class="card">
<h2>Craig Shoemaker</h2>
<ul>
<li><a href="https://twitter.com/craigshoemaker">Twitter</a></li>
</ul>
</section>
<section id="john-papa" class="card">
<h2>John Papa</h2>
</section>
document.getElementById("craig-shoemaker"); // matches by unique id
document.getElementById("john-papa");
document.querySelector(".card"); // returns the FIRST matching element
document.querySelectorAll(".card"); // returns ALL matching elements as a NodeList
document.querySelector('a[href="https://twitter.com/craigshoemaker"]');
querySelector/querySelectorAll accept the same kind of selector syntax used in CSS rules. When nesting quotes (a selector string containing an attribute value that is itself quoted), use single quotes on the outside and double quotes inside, or vice versa — just be consistent.
| Selection method | Returns |
|---|---|
document.getElementById(id) | A single element matching the given id |
document.querySelector(selector) | The first element matching a CSS-style selector |
document.querySelectorAll(selector) | A NodeList of every matching element |
Working with Element Attributes
Once an element is selected, you can read, set, or remove its attributes:
const links = document.querySelectorAll(".card a");
links.forEach(link => {
link.setAttribute("target", "_blank"); // opens each link in a new tab
});
setAttribute either creates a new attribute or updates an existing one. The complementary functions:
$0.getAttribute("target"); // reads a value ("$0" refers to the currently-selected DevTools element)
$0.getAttribute("href");
$0.removeAttribute("target"); // removes an attribute entirely
The classList API
The class attribute is special-cased in the DOM: because class is a reserved word in JavaScript, you cannot do element.class = .... Instead, the classList API manages an element’s classes:
const card = document.querySelector(".card");
card.classList.add("dark"); // adds a class ("card dark")
card.classList; // ["card", "dark"]
card.classList.remove("dark"); // removes it again ("card")
Adding a class such as dark that has a corresponding CSS rule (e.g. .dark { background-color: #222; color: #fff; }) instantly re-themes the element without touching the underlying HTML markup.
The Script Tag
The <script> element is where you execute JavaScript on a page. Its location in the document matters: because HTML and CSS are responsible for the structure and appearance of a page, and browsers process a page from top to bottom, <script> elements are conventionally placed near the very bottom of <body>, just before the closing </body> tag — this way, the page’s structure and styling are not held up waiting on a script.
<body>
<!-- page content -->
<script src="index.js" type="text/javascript"></script>
</body>
Moving JavaScript into its own external file (as with CSS) has two main benefits:
- Code is easier to write and maintain in a dedicated file, and multiple pages can share the same script.
- The browser can cache the file, so pages load faster after the first request.
Unlike some self-closing tags (such as <meta>), the <script> element always requires an explicit closing tag, even when it has no inline content and only references an external file via src.
The Window Object
The window object is the top-level context for scripts running in a browser tab; it hosts the document object (the DOM for that tab). Opening the same page in a second tab creates an entirely separate, independent window — even though both tabs show the same site.
window exposes many browser-level functions and properties, including window.location, which provides information about the current URL and its individual segments. In this course, window is primarily used to gain access to browser built-ins (as in the next module’s window.addEventListener('DOMContentLoaded', ...)).
Module 6: Building Bethany’s Pie Shop
This module puts everything from the previous modules into practice: HTML, CSS, and JavaScript are combined to build Bethany’s Pie Shop end to end. Key features of the finished page: a liquid layout that expands and contracts proportionally as the browser is resized, a mobile layout where the logo disappears and the hero image is cropped differently, and a working order-total calculator.
Project Setup and HTML Boilerplate
Starting from a blank index.html, typing the Emmet abbreviation html:5 and pressing Tab scaffolds a base HTML5 document:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
</html>
Breaking down the boilerplate:
| Line/Element | Purpose |
|---|---|
<!DOCTYPE html> | Declares the document as HTML5 |
<html lang="en"> | The root element; lang="en" tells search engines and tools the page’s primary language |
<head> | Holds metadata: title, character set, viewport, and other non-visible information |
<meta charset="UTF-8"> | Character encoding, supporting a very wide range of characters and languages |
<meta name="viewport" ...> | Controls initial scaling/rendering width on mobile devices |
<body> | The visible content of the page |
For Bethany’s Pie Shop, the <title> is updated and a favicon link is added:
<title>Bethany's Pie Shop</title>
<link rel="icon" type="image/png" href="favicon.png">
Building the Header and Navigation
The page header uses Emmet’s header>nav>ul>li*3>a shorthand to scaffold a <header> containing a <nav>, an unordered list, and three list items each with a link. The first list item is then swapped out for Bethany’s logo image instead of a text link:
<header>
<nav>
<ul>
<li>
<img src="images/logo.png"
width="150"
height="47"
alt="Bethany's Pie Shop company logo">
</li>
<li><a href="pies.html">Pies</a></li>
<li><a href="about.html">About</a></li>
</ul>
</nav>
</header>
The pies.html and about.html destination pages are referenced but not built as part of this course — they simply illustrate how to link between pages within a site.
Building the Footer Markup
The site footer uses an Emmet expression combining footer>address>(h2+p+p) — the + sign (rather than >) places sibling elements next to each other instead of nesting them:
<footer>
<address>
<h2>Bethany's Pie Shop</h2>
<p>Bakery Street 555</p>
<p>Brussels, Belgium</p>
</address>
<p>All rights reserved © 2023 Bethany's Pie Shop</p>
<p>Contact us via <a href="mailto:info@bethanyspieshop.com">email</a></p>
</footer>
The Aside and Logo Column
Looking at the finished layout, the page is split into two columns: a left column with Bethany’s icon logo, and a right column with the main content. On mobile, the left column disappears entirely. The structure to support this two-column behavior uses the semantic <main>, <article>, and <aside> elements:
<main>/<article>signal to search engines that this is the most important content on the page.<aside>signals that its content — here, the decorative logo — is not as important to the page’s core message.
<aside>
<div class="logo-container">
<img src="images/bethanylogo.png"
alt="Bethany's Pie Shop Logo"
width="120" height="140">
</div>
</aside>
The Responsive Picture Element
The hero image crops differently depending on device size — not simply hidden/shown or resized, but an entirely different image file is chosen by the browser for different screen widths. This is done using the <picture> element together with a media-query-driven <source>:
<picture>
<source media="(min-width: 1000px)"
srcset="images/bethany-700.jpg" />
<img src="images/bethanysmall.jpg" alt="Bethany of Bethany's Pie Shop" />
</picture>
The <picture> element is a container that can hold several candidate image sources, each constrained by attributes like resolution, format, or a media query. The browser evaluates each <source>’s media attribute in order and renders the first one that matches the screen; if none match, it falls back to the plain <img> child. Here, if the viewport is 1000px or wider, bethany-700.jpg is used; otherwise the browser falls back to the smaller bethanysmall.jpg.
Title Text and the Order Calculator Table
The article’s heading and body copy are added, followed by the Order Calculator table. A table is built from rows (<tr>) and cells — header cells (<th>) and data cells (<td>):
<article>
<h1>Welcome to Bethany's Pie Shop</h1>
<picture><!-- as above --></picture>
<h2>Our history</h2>
<p>For many years, Bethany has been baking the most delicious pies at her home.</p>
<div id="calculator">
<h2>Order Calculator</h2>
<table>
<thead>
<tr>
<th>Type</th>
<th>Price</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
<tr>
<td>Apple</td>
<td>$12.00</td>
<td><input type="number" id="apple" min="0"></td>
</tr>
<tr>
<td>Cherry</td>
<td>$15.00</td>
<td><input type="number" id="cherry" min="0"></td>
</tr>
<tr>
<td>Strawberry</td>
<td>$18.00</td>
<td><input type="number" id="strawberry" min="0"></td>
</tr>
<tr>
<td></td>
<td>Total:</td>
<td id="total-container">$0.00</td>
</tr>
</tbody>
</table>
</div>
</article>
Each pie’s <input type="number" min="0"> restricts entry to numeric, non-negative values. The blank first cell in the totals row exists purely to keep the table’s three-column structure consistent even though it has no content. The id values (apple, cherry, strawberry, total-container) are deliberately chosen so JavaScript can locate and update them later.
Linking the CSS File
Rather than styling inline, all CSS rules are placed in an external styles.css file, linked from the <head>:
<head>
<link rel="stylesheet" href="styles.css">
</head>
Placing rules in a separate .css file allows the file to be cached by the browser (making the site faster) and keeps rules easier to write, read, and maintain than styles embedded directly in the markup.
CSS Custom Properties (Variables)
Just as JavaScript has variables, CSS supports custom properties (“CSS variables”) — reusable named values declared once and referenced throughout a stylesheet. They are declared under a :root selector so they are available everywhere:
:root {
--color-brand-primary: #a1603b;
--color-brand-contrast: #fff;
--color-medium-gray: #898989;
--color-light-gray: #ccc;
--color-background: #E5E5E5;
--font-body: 'Segoe UI', Arial, Helvetica, sans-serif;
}
A custom property name is arbitrary but must be prefixed with --. It is referenced elsewhere using the var() function:
header {
background-color: var(--color-brand-primary);
}
Visual Studio Code shows a small color preview next to recognized hex values, which is a handy visual aid while tuning a palette like this one.
Resetting Browser Defaults
Different browsers ship with slightly different default styles (padding on <body>, margin on <html>, and so on). To get a consistent baseline across browsers, the base elements are reset to 0 margin/padding and given the shared body font and background color:
body, html {
margin: 0;
padding: 0;
font-family: var(--font-body);
background-color: var(--color-background);
}
This removes any unwanted default spacing around the edges of the page and establishes the font and background used throughout the rest of the site.
Styling the Navigation
The <nav> list is reset (removing default list spacing) and then styled so each link behaves as an inline navigation item rather than a stacked list:
nav ul {
margin: 0;
padding: 0;
}
nav > ul > li {
display: inline-block;
list-style: none;
margin: 0 1.5em 0 1.5em;
vertical-align: middle;
}
nav > ul > li:first-child {
margin: .5em;
padding: 0;
}
nav ul li a,
nav ul li a:active,
nav ul li a:hover,
nav ul li a:visited {
color: var(--color-brand-contrast);
text-decoration: none;
text-transform: uppercase;
font-weight: 600;
font-size: 1.25em;
}
Key details:
- The
>child combinator restricts the rule to list items that are direct children of the navigation’s unordered list. display: inline-blocklets list items sit side by side while still allowingwidth,height,margin, andpaddingto be controlled — a hybrid ofinline(which ignores box dimensions) andblock(which forces its own line).list-style: noneremoves the default bullet markers, appropriate for a navigation menu.- The margin shorthand
0 1.5em 0 1.5emfollows the CSS order top, right, bottom, left. - Stacking the link states (
a,a:active,a:hover,a:visited) into one rule normalizes link appearance so navigation links do not change color when clicked or visited — unlike typical body-text links. 1.25emis a relative unit:1emequals the parent element’s font size, so1.25emrenders at 125% of that inherited size.
Styling the Heading
A small amount of color and spacing rounds out the header area and prepares the <article> for the layout rules that follow:
h1 {
color: var(--color-brand-primary);
}
article {
padding: 2em;
}
Styling the Order Calculator
The calculator’s container gets a subtle shadow, rounded corners, a constrained width, and background color, all driven by the custom properties defined earlier:
#calculator {
box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.2);
border: solid 1px var(--color-medium-gray);
border-radius: 1em;
padding: 1.5em;
width: 90%;
max-width: 325px;
background-color: var(--color-brand-contrast);
margin-top: 2em;
}
#calculator h2 {
margin-top: 0;
}
#calculator div {
margin: .5em 0 .5em 0;
}
#calculator input {
padding: 5px;
border-radius: 3px;
border: solid 1px var(--color-medium-gray);
width: 150px;
}
box-shadow value | Meaning |
|---|---|
2px (1st) | Horizontal offset of the shadow |
2px (2nd) | Vertical offset of the shadow |
8px | Blur radius — how far the shadow spreads/softens |
rgba(0, 0, 0, 0.2) | Shadow color: black at 20% opacity (rgba = red, green, blue, alpha/transparency) |
width: 90% lets the calculator scale with its container on small screens, while max-width: 325px prevents it from growing too large on wide screens.
Styling the Footer
footer {
margin: 3em 0;
padding: 1em;
background-color: var(--color-light-gray);
}
footer address {
font-style: normal;
}
footer address h2,
footer address p {
margin: 0;
}
footer address h2 {
font-size: 1.25em;
}
Browsers italicize the <address> element by default; font-style: normal overrides that. Removing the default margins from the heading and paragraphs inside the footer’s address block makes those lines stack tightly rather than with large gaps between them, and the shop name is bumped to 1.25em for a bit more visual emphasis.
Building the Responsive Layout with Flexbox
The mobile-first baseline hides the logo container entirely, since the logo is unnecessary overhead on a small screen:
.logo-container {
display: none;
}
A media query then activates the desktop, two-column layout once the viewport reaches 960px:
@media (min-width:960px) {
main {
display: flex;
flex-direction: row-reverse;
}
article, aside {
display: block;
flex-basis: 0;
flex-shrink: 1;
padding: 0.75rem;
}
article {
flex: 4;
padding-right: 6em;
}
aside {
flex: 1;
}
footer {
padding-left: 20%;
}
.logo-container {
display: block;
text-align: center;
padding-top: 2em;
}
.logo-container img {
max-width: 120px;
}
}
Explaining the key choices:
flex-direction: row-reverse— in the HTML,<article>(main content) comes before<aside>(logo) so search engines encounter the important content first. Visually, however, the design calls for the logo on the left.row-reverseflips the visual order without changing the semantic/source order.flex-basis: 0on both columns means neither starts with a predefined size — both grow purely according to theflexratio given to each.flex-shrink: 1allows both columns to shrink proportionally if the container is too narrow to fit their full preferred size.flex: 4on<article>andflex: 1on<aside>divide the row into 5 total units — the article claims 4 of them, the aside claims 1 — producing the wide-content / narrow-logo column split seen in the final design.rem(“root em”) is used for some padding values so that if the root font size ever changes globally, this padding scales proportionally with it.- Inside the media query,
.logo-containerswitches fromdisplay: nonetodisplay: block(revealing it only on desktop), is centered withtext-align: center, and its image is capped atmax-width: 120pxso it never grows too large as the screen widens.
flowchart LR
subgraph Desktop["Desktop layout (>= 960px), main { flex-direction: row-reverse }"]
direction LR
Aside["aside (logo)<br/>flex: 1"] --- Article["article (content)<br/>flex: 4"]
end
flowchart TD
M["main"] -->|"< 960px"| Stacked["Single column: logo hidden,<br/>article content only"]
M -->|">= 960px"| TwoCol["Two-column Flexbox row:<br/>logo (1 unit) + content (4 units)"]
With these rules in place, resizing the browser window shows the layout staying proportional (a “liquid” layout), while the mobile emulation view shows the simpler, single-column, logo-free presentation.
Linking the Script File
The JavaScript file is referenced with a <script> tag placed just before the closing </body> tag, ensuring the HTML content is fully loaded into the browser before the script runs:
<script src="script.js"></script>
</body>
</html>
Keeping JavaScript in its own external file (as with CSS) enables browser caching, makes debugging easier, and lets the editor provide better tooling for a file dedicated to one type of code.
Modeling the Inventory Object
Bethany’s inventory of pies is modeled as a single JavaScript object, with a nested object per pie type tracking its price and the customer’s requested quantity:
const inventory = {
apple: { price: 12, qty: 0 },
cherry: { price: 15, qty: 0 },
strawberry: { price: 18, qty: 0 }
};
Individual values can be read either with dot notation (inventory.apple.price) or bracket notation (inventory["apple"].price) — the bracket form becomes essential once the property name itself is stored in a variable, as it is in the calculation logic that follows.
The Sum Function
The sum function calculates the running order total across every pie in the inventory:
function sum() {
let total = 0;
const keys = Object.keys(inventory);
keys.forEach(key => {
total += inventory[key].price * inventory[key].qty;
});
return total;
}
Rather than hard-coding inventory.apple, inventory.cherry, and inventory.strawberry explicitly (which would break the moment a new pie type, such as pumpkin, is added), the function asks the inventory object for its own property names via Object.keys(inventory). It then uses forEach with an arrow function (key => { ... }, a more concise alternative to a full function (key) { ... } expression) to loop over every key, using bracket notation (inventory[key]) to look up each pie’s price and quantity dynamically.
The total += ... shorthand is equivalent to total = total + ... — it accumulates a running total across each iteration of the loop, rather than overwriting total with only the last pie’s contribution.
sequenceDiagram
participant caller as Calling code
participant sum as sum()
participant inv as inventory object
caller->>sum: sum()
sum->>inv: Object.keys(inventory)
inv-->>sum: ["apple", "cherry", "strawberry"]
loop for each key
sum->>inv: inventory[key].price * inventory[key].qty
inv-->>sum: partial amount
sum->>sum: total += partial amount
end
sum-->>caller: return total
The Calculate Function
The calculate function is called whenever a customer changes the quantity in one of the input boxes:
function calculate(box) {
let qty = 0;
if(box.value.length > 0) {
qty = parseInt(box.value);
}
inventory[box.id].qty = qty;
const total = sum();
return `$${total}.00`;
}
Key details:
boxis the specific<input>element that triggered the change.- Even though the
<input>is declaredtype="number", its.valueis always read as a string;box.value.length > 0checks whether the user has typed anything at all. parseInt(box.value)converts that string into an actual number so it can be used in arithmetic.- Because each input’s
id(apple,cherry,strawberry) exactly matches a key in theinventoryobject,inventory[box.id].qty = qty;updates the correct pie’s quantity dynamically, without needing anif/elsechain to figure out which pie changed. - The final total is formatted as a dollar amount using a template string:
`$${total}.00`.
Listening for DOMContentLoaded
Rather than running immediately (before the page’s HTML elements exist), the remaining setup code waits for the DOMContentLoaded event — fired once the HTML has been parsed and is available to JavaScript:
window.addEventListener('DOMContentLoaded', () => {
const totalContainer = document.getElementById('total-container');
const inputBoxes = document.querySelectorAll('#calculator input');
// event wiring goes here, see next section
});
Wrapping the element lookups and event wiring inside this listener ensures they only run once the page has finished loading — the inventory, sum, and calculate functions/objects, by contrast, can be declared at the top level since they do not depend on the DOM being ready.
Wiring Up Box Event Listeners
querySelectorAll returns a NodeList (not a true array, but one that does support forEach) containing every quantity input box inside the calculator. Each box is given listeners for both the change and keyup events:
window.addEventListener('DOMContentLoaded', () => {
const totalContainer = document.getElementById('total-container');
const inputBoxes = document.querySelectorAll('#calculator input');
inputBoxes.forEach(box => {
box.addEventListener('change', () => {
totalContainer.textContent = calculate(box);
});
box.addEventListener('keyup', () => {
totalContainer.textContent = calculate(box);
});
});
});
Both event types matter for a good user experience:
| Event | Fires when | Why it is needed here |
|---|---|---|
change | The input loses focus after its value changed | Catches cases like using arrow keys or losing focus without a keystroke |
keyup | Any key is released while typing in the box | Updates the total live, as the customer types, instead of waiting until they click or tab away |
sequenceDiagram
participant User
participant Box as input#apple
participant Listener as keyup/change listener
participant Calc as calculate(box)
participant Sum as sum()
participant Total as #total-container
User->>Box: types "2"
Box-->>Listener: keyup event fires
Listener->>Calc: calculate(box)
Calc->>Calc: parseInt(box.value) -> 2
Calc->>Calc: inventory[box.id].qty = 2
Calc->>Sum: sum()
Sum-->>Calc: total (number)
Calc-->>Listener: "$total.00"
Listener->>Total: textContent = "$total.00"
Debugging the Calculator Step by Step
Stepping through the finished calculator with the browser’s debugger illustrates exactly how the pieces fit together:
- Set a breakpoint on the first line inside the
DOMContentLoadedhandler, inside theforEachloop wiring up event listeners, and inside thekeyuphandler. - Reload the page — execution pauses at
DOMContentLoaded. Stepping over confirmstotalContainernow references the element withid="total-container". querySelectorAll('#calculator input')returns all three quantity boxes; stepping into theforEachcall shows thechangeandkeyuplisteners being attached, once per box (three times total).- Typing a value (for example,
1) into the Apple box fireskeyup; stepping intocalculate(box)showsbox.value.lengthevaluating to1(greater than0), soparseInt(box.value)converts the string"1"into the number1. inventory[box.id].qty = qty;updatesinventory.apple.qtyfrom0to1, confirmed by inspecting theinventoryobject.- Stepping into
sum()showstotalstarting at0, then accumulating12for the apple pie (price 12 * qty 1),0for cherry (qtystill0), and0for strawberry — the running total settles at12. - Back in
calculate, the template string formats the result as"$12.00", which is written into#total-container’stextContent, and the calculator visibly displays $12.00.
This walkthrough demonstrates the complete flow: an HTML input event → a JavaScript event listener → the calculate function → the sum function reading the inventory object → a formatted string written back into the DOM.
Summary
This course walked through the three foundational technologies of the web — HTML, CSS, and JavaScript — building up from a single <h1> element to a complete, responsive, interactive website: Bethany’s Pie Shop.
Key principles to carry forward:
- HTML provides structure and meaning. Elements are made of tags and attributes; using the right semantic element (e.g.
<h1>vs. a plain<p>,<main>vs.<aside>) communicates significance to both browsers and search engines, in addition to giving the browser a structure to render — represented internally as the DOM, a tree of objects. - CSS provides style, layout, and responsiveness. Selectors (tag, class, ID, attribute, combinators) target elements; rules cascade based on source order and specificity; some properties inherit and some do not; the box model (content, padding, border, margin) governs every element’s rendered size; Flexbox and media queries together produce layouts that adapt fluidly from mobile to desktop.
- JavaScript provides interactivity. Variables (
let/const), operators, control flow (if/else), functions (with arguments and return values), object literals, arrays, and loops (while,for,forEach) are the building blocks; the DOM can be queried (getElementById,querySelector(All)), read and modified (attributes,classList), and wired up to user interaction through events (DOMContentLoaded,change,keyup). - The browser’s developer tools are indispensable at every stage — inspecting the DOM and Styles panel, live-editing CSS, and stepping through JavaScript with breakpoints all shorten the feedback loop dramatically compared to guessing, saving, and reloading.
- Validate and check compatibility as you go — the W3C validator confirms your HTML follows the specification, and caniuse.com confirms a CSS feature is safe to rely on across target browsers.
Quick-Reference: Core Concepts by Technology
| Technology | Core building blocks | Key tools |
|---|---|---|
| HTML | Elements, tags, attributes, semantic structure, the DOM | validator.w3.org, browser Elements panel |
| CSS | Selectors, specificity, cascade, inheritance, box model, Flexbox, media queries | caniuse.com, browser Styles panel |
| JavaScript | Variables, operators, control flow, functions, objects, arrays, loops, DOM selection, events | Browser Console, Sources panel/debugger |
Completion Checklist
- Comfortable writing valid HTML5 boilerplate and common elements (headings, paragraphs, links, images, tables, semantic sectioning elements).
- Understand the difference between an element’s tag and its attributes, and the special roles of
idandclass. - Able to explain the DOM as the browser’s in-memory tree representation of a page.
- Can write CSS selectors of every major type (tag, class, ID, attribute, list, child combinator) and predict which rule wins under the cascade and specificity rules.
- Understand the box model (content, padding, border, margin) and the effect of
box-sizing: border-box. - Can build a simple responsive, multi-column layout using Flexbox and media queries, mobile-first.
- Comfortable with core JavaScript: variables, operators, conditionals, functions (arguments and return values), object literals, arrays, and all three loop styles.
- Can select DOM elements and read/update attributes and classes from JavaScript.
- Can wire up event listeners (
DOMContentLoaded,change,keyup) to make a page interactive. - Comfortable using browser DevTools to inspect elements, live-edit styles, and step through JavaScript with breakpoints.
Search Terms
html · css · fundamentals · web · frontend · development · selectors · function · element · elements · object · responsive · styling · browser · calculator · javascript · style · anatomy · arguments · attributes · bethany · box · control · debugging