Table of Contents
- Module 1: Introduction and Environment Setup
- Module 2: Setting up the Page Structure
- Module 3: Adding Text, Headings, and Images to the Home Page
- Module 4: Moving to CSS
- Module 5: Defining the Page Layout and Structure
- Module 6: Working with Images
- Module 7: Creating the Pie Overview Table
- Module 8: Designing the Pie Detail Page
- Module 9: Adding Navigation to the Site
- Module 10: Creating a Contact Form
- Module 11: Including External Content Using iframe
- Module 12: Storing Data with JavaScript
- Summary
This manual walks through building a complete, real-world website from scratch using only HTML and CSS, with a small dose of JavaScript at the end. Throughout the material, a single running example is used: the website for a fictional bakery called Bethany’s Pie Shop. Every concept introduced — page structure, text, images, tables, semantic layout, navigation, forms, embedded content, and browser storage — is applied directly to building out this site, page by page, until a fully working, multi-page, styled website exists.
flowchart LR
A[Module 1: Setup] --> B[Module 2: Page Structure]
B --> C[Module 3: Text, Headings, Images]
C --> D[Module 4: Moving to CSS]
D --> E[Module 5: Semantic Layout]
E --> F[Module 6: Images]
F --> G[Module 7: Tables]
G --> H[Module 8: Detail Page / Inline Elements]
H --> I[Module 9: Navigation]
I --> J[Module 10: Forms]
J --> K[Module 11: iframe]
K --> L[Module 12: JavaScript Storage]
Module 1: Introduction and Environment Setup
What We Will Build
The site built throughout this course is for a bakery, Bethany’s Pie Shop, which needs an online presence to showcase its selection of pies to customers. The site is built page by page across the modules:
- A home page — the page visitors see first, containing a welcome message, history, and promotions.
- A pie overview page — listing the full catalog of pies in a table.
- A pie detail page — showing details for a single pie (description, steps, ingredients).
- A contact page — containing a contact form.
- A navigation menu — linking all the pages together.
- An about page — containing an embedded video.
- Small JavaScript-driven interactivity — allowing a visitor to mark a pie as a favorite, persisted using the browser’s Local Storage API.
mindmap
root((Bethany's Pie Shop))
Home page
Welcome text
History
Weekly promotions
Pie overview page
Table of all pies
Pie detail page
Description
Steps
Ingredients
Contact page
Contact form
About page
Embedded video
Navigation
Top menu
Sidebar links
Required Tools
To build the site, the following tools are used:
| Tool | Purpose |
|---|---|
| A text editor (Visual Studio Code used throughout) | Writing HTML, CSS, and JavaScript code |
| A “Live Server” style editor extension | Automatically serves and refreshes the page in the browser on every save |
| A web browser | Viewing and testing the rendered site |
The operating system does not matter — HTML is rendered identically everywhere, and the chosen editor is available cross-platform.
Demo: Setting up Your Environment
- Install a code editor (e.g., Visual Studio Code).
- Open the editor’s extension marketplace and search for a live-reloading server extension (e.g., “Live Server”), then install it.
- Enable Auto Save in the editor so changes are saved automatically and immediately reflected in the browser through the live server.
With the environment ready, the next step is creating the actual folder and first file for the site.
Module 2: Setting up the Page Structure
Understanding HTML
HTML (HyperText Markup Language) is used to define the structure and content of a web page — headings, paragraphs, images, links — while intentionally leaving appearance (colors, fonts, spacing) to CSS. HTML is a markup language, not a programming language: it consists of readable, plain-text tags that a browser parses and renders.
Key facts about HTML:
- The current specification is referred to as the HTML Living Standard, maintained by a group of browser vendors (Microsoft, Apple, Google, and others). “HTML5” was the last numbered version (2012); the standard now evolves continuously without new version numbers.
- Files use the
.htmor.htmlextension and are plain text — any text editor, or even a browser, can open them. - A page is composed of elements. Most elements have an opening tag and a matching closing tag (prefixed with
/), with content between them:
<h1>My Heading</h1>
- Elements are commonly nested inside one another to build up a page’s structure:
<div>
<h1>Title</h1>
</div>
- HTML tags are conventionally written in lowercase, although HTML itself is case-insensitive.
- Extra information on an element is provided via attributes, placed inside the opening tag, separated by spaces, with no space around the
=sign:
<img src="pie.jpg" width="200">
Attributes, like elements, are case-insensitive but conventionally lowercase.
flowchart TD
A[HTML File] --> B["Elements (tags)"]
B --> C[Attributes provide extra info]
B --> D[Nesting builds structure]
A --> E[Browser parses & renders]
Creating the HTML Page Structure
Every HTML page follows the same base structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Page Title</title>
</head>
<body>
<!-- visible content goes here -->
</body>
</html>
| Part | Purpose |
|---|---|
<!DOCTYPE html> | Declares the document type; tells the browser to apply modern HTML standards. Not itself an HTML tag, and must be the very first thing in the file. Conventionally uppercase. |
<html> | Root element wrapping all page content. The optional lang attribute helps screen readers identify the document’s language. |
<head> | Contains metadata about the page: character set, title, other meta tags, links to stylesheets, and scripts. Not rendered directly as page content. |
<meta charset="utf-8"> | Required meta element specifying the character encoding; conventionally the first item in <head>. |
<title> | Required, and should appear only once; text shown in the browser tab/window title and used by search engines. Can only contain plain text. |
<body> | Contains all visible page content — text, images, links, tables, etc. |
Other elements commonly placed in <head> include additional <meta> tags (author, description, viewport), <link> (referencing an external stylesheet), <style> (inline CSS block), and <script> (JavaScript code).
Demo: Creating the Page Skeleton
- Create a project folder (e.g.,
BethanysPieShop) and open it in the editor — most editors organize work around folders. - Create a new file named
index.html.indexis the conventional default filename served by web servers when a site’s root URL is requested (e.g.,bethanyspieshop.comservesindex.htmlautomatically). - Add the doctype, then the
htmlelement, thenheadandbody:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Bethany's Pie Shop</title>
<meta name="author" content="Bethany's Pie Shop">
<meta name="description" content="Store front for Bethany's Pie Shop">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
</body>
</html>
- Right-click inside the HTML file and choose the live-server option to open the page in the browser. At this stage, only the page title shows in the browser tab — the body is still empty, since only the skeleton has been created so far. Whitespace, indentation, and line breaks between tags have no effect on the rendered output; they exist purely to keep the source readable.
Module 3: Adding Text, Headings, and Images to the Home Page
Working with Headings
HTML provides six levels of built-in heading elements, <h1> through <h6>, with <h1> being the largest/most important and <h6> the smallest. Each level receives different default styling from the browser.
<h1>Largest heading</h1>
<h2>Second level</h2>
<h3>Third level</h3>
| Heading | Typical use |
|---|---|
<h1> | Main page title — normally used only once per page |
<h2>–<h3> | Section headings |
<h4>–<h6> | Rarely used sub-section headings |
Headings can contain plain text, but also nested elements such as links or images. Screen-reading software relies on heading levels to understand a page’s structure and importance, so headings should be used semantically, not just for visual size.
Adding Paragraphs
Textual content is placed inside <p> (paragraph) elements. The browser automatically adds vertical margin (spacing) before and after each paragraph:
<p>Welcome to Bethany's Pie Shop.</p>
<p>We have been baking delicious pies since 2013.</p>
To force a line break within a paragraph without starting a new paragraph, use the empty, self-closing <br> element:
<p>Line one<br>Line two</p>
Multiple consecutive paragraphs each get their own margin spacing automatically.
Styling Text
HTML historically offered a handful of elements/attributes for basic text styling, most of which are superseded by CSS today:
| Element/Attribute | Effect |
|---|---|
<b> | Bold text (purely visual, no semantic meaning) |
<i> | Italic text (purely visual) |
<em> | Emphasizes text (semantic — usually rendered italic) |
<strong> | Strongly emphasizes text (semantic — usually rendered bold) |
<code> / <samp> | Renders text in a monospace “code” font |
<kbd> | Represents keyboard input |
<var> | Represents a variable |
align attribute on headings | Legacy way to align text; discouraged today |
Rather than these, the recommended approach is the style attribute, which accepts a semicolon-separated list of CSS property/value pairs directly on an element:
<h1 style="font-size: 40px; color: #ff6600;">Bethany's Pie Shop</h1>
Inline styles work but are not reusable — if many elements need the same look, or the look needs to change later, every occurrence must be edited individually. A better approach is to group styles in a <style> block in the <head>:
<head>
<style>
h1 {
font-size: 40px;
color: #ff6600;
}
</style>
</head>
The best approach — introduced formally in the next module — is moving all style rules into a separate CSS file, linked from the page via <link rel="stylesheet" href="styles.css">.
Demo highlights (styling the welcome page before CSS was introduced):
<body style="background-color: #fef6f5; font-family: Verdana, sans-serif;">
<h1 style="font-size: 40px; color: #c87d52;">Welcome to <b>Bethany's Pie Shop</b></h1>
<p style="font-weight: bold; color: brown;">
<i>Delicious pies baked fresh every day.</i>
</p>
<small>©2023 Bethany's Pie Shop</small>
</body>
Adding a First Image
Images are added with the empty, self-closing <img> element. The src attribute points to the image file, and the alt attribute provides a text description used by screen readers:
<hr>
<h3>Our weekly promotions</h3>
<h4>Cheesecake</h4>
<img src="cheesecake.jpg" width="500" height="300" alt="A delicious cheesecake"><br><br>
The <hr> element draws a horizontal rule/divider line. At this point the home page already contains a heading, paragraphs with inline styling, and a first image — though it is still a single unstyled block with no real page layout.
Module 4: Moving to CSS
Introducing CSS
CSS (Cascading Style Sheets) separates the presentation of a page from its content (HTML). Instead of scattering style attributes throughout the markup, style rules are grouped in a dedicated .css file, referenced from the HTML <head>:
<head>
<link rel="stylesheet" href="styles.css">
</head>
A CSS file contains one or more rules, each following the same syntax:
.header {
color: red;
border: 1px solid black;
}
| Part | Meaning |
|---|---|
.header | The selector — determines which HTML elements the rule applies to |
{ } | Contains one or more declarations |
color: red; | A declaration: a CSS property, a colon, a value, and a terminating semicolon |
The “C” in CSS — cascading — refers to the fact that multiple rules can match the same element, and styles are inherited down through nested elements (the DOM, or Document Object Model) unless overridden by a more specific rule closer to the element. Some elements (like headings) have built-in default styles with higher cascade priority than inherited values.
flowchart TD
Body["body { font-size: 12px; }"] --> Div1["div { color: red; } → inherits font-size 12px, adds red"]
Div1 --> Div2["div { padding: 10px; } → inherits font-size + color, adds padding"]
Div2 --> P["p { font-size: 10px; color: blue; } → overrides both, wins by proximity"]
Body --> H1["h1 { color: green; } → keeps browser-default font-size, overrides color"]
Comments in CSS use /* ... */ and can span multiple lines.
Working with CSS Selectors
CSS selectors determine which HTML elements a rule targets.
| Selector type | Syntax | Matches |
|---|---|---|
| Element selector | h2 { } | All <h2> elements |
| ID selector | #header { } | The element with id="header" (should be unique per page) |
| Class selector | .header { } | All elements with class="header" (can be reused on many elements) |
| Attribute selector | a[title] { } | All <a> elements that have a title attribute |
| Universal selector | * { } | Every element |
| Descendant combinator | .main a { } | All <a> elements nested anywhere inside an element with class main |
| Child combinator | div > .header { } | Elements with class header that are a direct child of a div |
| Grouping (comma) | .main, .header { } | Applies the same rule to both selectors |
| Pseudo-class | a:hover { } | An element in a particular state (link, visited, hover, active) |
Common CSS properties introduced at this stage:
color: #333333;
background-color: #fef6f5;
background-image: url(background.png);
background-repeat: no-repeat;
font-size: 15px;
text-decoration: underline;
Applying CSS Styles on Text
Additional text-focused CSS properties:
| Property | Purpose | Example |
|---|---|---|
font-size | Text size — prefer relative units (em) over pixels for better scaling across devices | font-size: 1.5em; |
color | Text color (not font-color) | color: #c87d52; |
font-weight | Boldness | font-weight: bold; |
text-decoration | Underline, etc. | text-decoration: underline; |
text-transform | Upper/lowercase transform | text-transform: uppercase; |
font-variant | Small caps | font-variant: small-caps; |
text-align | Horizontal alignment | text-align: center; |
line-height | Vertical spacing between lines | line-height: 1.5em; |
Custom web fonts are declared with @font-face:
@font-face {
font-family: WorkSansBold;
src: url(fonts/WorkSans-Bold.ttf);
}
@font-face {
font-family: WorkSansRegular;
src: url(fonts/WorkSans-Regular.ttf);
}
h1 {
font-family: WorkSansBold;
}
Demo: Recreating the Home Page Using CSS
The inline styles from the previous module are progressively moved into styles.css:
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
h1 {
font-size: 2em;
color: #c87d52;
font-family: WorkSansBold;
}
h2, h3, h4 {
font-family: WorkSansRegular;
}
#promo-header {
text-transform: uppercase;
}
Key points demonstrated:
- Element selectors (
body,h1) apply globally across every page linking the same stylesheet. - Grouped selectors (
h2, h3, h4) apply one rule to several elements at once, while cascading still allows further per-element overrides (e.g., addingfont-weight: boldspecifically toh2). - Custom IDs (e.g.,
id="promo-header") let a single specific element receive its own targeted rule. - Switching a heading’s font-size from pixels to
emvalues makes text scale proportionally rather than using a fixed absolute size.
Module 5: Defining the Page Layout and Structure
The HTML Content Model
Prior to HTML5, elements were categorized simply as inline (placed on the current line, only as wide as their content) or block (always starting a new line, expanding to available width). The current, more fine-grained content model organizes elements into overlapping categories based on meaning, not just layout behavior:
| Category | Purpose | Example elements |
|---|---|---|
| Metadata content | Presentation/behavior of the document, or links to external resources | <link>, <style>, <title> |
| Flow content | Most elements used in the document body | images, links, sections, paragraphs |
| Sectioning content | Organizes the page into logical regions | <article>, <section>, <nav>, <aside> |
| Phrasing content | Elements related to text | <span>, <em>, <strong> |
| Heading content | Defines headers | <h1>–<h6> |
| Embedded content | Brings in external resources | <img>, <video>, <iframe> |
| Interactive content | Elements designed for interaction | <a>, form controls |
HTML remains focused purely on giving meaning to content; layout and appearance stay the responsibility of CSS.
Working with Semantic Elements
Rather than building every page section from generic, meaningless <div> blocks distinguished only by id, HTML provides semantic elements that describe the role of a page region:
| Semantic element | Role |
|---|---|
<header> | Container for intro content or navigational links (can appear more than once, e.g., inside <article> too) |
<nav> | Contains the main navigational links/menu |
<main> | The main, unique content of the page |
<article> | Standalone content (e.g., a blog post, a story) |
<section> | A generic section of content (not for pure layout — use <div> for that) |
<aside> | Content related to, but separate from, the main content (e.g., a sidebar) |
<footer> | Footer content — copyright, contact info |
<address> | Contact/location information |
<div> | Generic, non-semantic block — still used, but only for layout purposes |
flowchart TD
HTML[html] --> HEAD[head]
HTML --> BODY[body]
BODY --> HEADER["header#main-header (logo + nav)"]
BODY --> MAINCONTENT["div#main-content"]
MAINCONTENT --> ASIDE["aside#left-menu (sidebar links)"]
MAINCONTENT --> MAIN["main#main"]
MAIN --> ARTICLE["article (welcome text)"]
MAIN --> SECTION["section#promos (promotions)"]
BODY --> FOOTER["footer (copyright, contact)"]
Using semantic elements does not, by itself, change how a page looks — browsers apply no special layout to <header>, <aside>, <main>, etc. It does, however, give both browsers and assistive technology (screen readers) a correct understanding of what each region means.
Demo: Creating the Page Structure
The home page markup is restructured around semantic elements:
<body>
<header id="main-header">
<nav>
<!-- navigation links go here -->
</nav>
</header>
<aside id="left-menu">
<header>
<p>Browse our pies</p>
</header>
</aside>
<main id="main">
<article>
<header>
<h1>Welcome to Bethany's Pie Shop</h1>
</header>
<p>...</p>
<h2>Our history</h2>
</article>
<section id="promos">
<h3>Our weekly promotions</h3>
<img src="cheesecake.jpg" width="500" height="300">
</section>
</main>
<footer>
<p>Our address is <address>Bethany's Pie Shop - Bakery Street 555 Brussels Belgium</address></p>
</footer>
</body>
At this stage the page has correct semantic structure but still no real visual layout — arranging the blocks on screen is the job of CSS, covered next.
The CSS Box Model: Padding and Margin
Every HTML element is rendered as a rectangular box composed of four concentric parts:
flowchart TD
subgraph Margin
subgraph Border
subgraph Padding
Content[Content]
end
end
end
| Part | Description |
|---|---|
| Content | The actual text/image/element |
| Padding | Space between the content and the border (inside the border) |
| Border | A visible or invisible outline around the padding+content |
| Margin | Space between this element’s border and neighboring elements (outside the border) |
div {
margin: 10px; /* space outside the box, on all four sides */
padding: 10px; /* space inside the box, around the content */
}
Per-side properties are also available: margin-top, margin-right, margin-bottom, margin-left (and the equivalent padding-* properties). CSS also supports a shorthand notation for setting multiple sides at once:
| Values given | Meaning |
|---|---|
margin: 10px; | All four sides = 10px |
margin: 10px 20px; | Top & bottom = 10px, left & right = 20px |
margin: 0 10px auto; | Top = 0, bottom = 10px, left & right = auto (equal, i.e. horizontally centered) |
margin: 10px 20px 30px 40px; | Top, right, bottom, left (clockwise) |
Setting margin: auto on the left and right of a block-level element with a fixed width horizontally centers it within its parent.
Positioning Elements on the Page
The CSS position property controls how an element is placed:
| Value | Behavior |
|---|---|
static (default) | Normal document flow; top/left/etc. have no effect |
relative | Positioned relative to where it would normally sit, using top/left/right/bottom, without affecting sibling elements |
fixed | Positioned relative to the browser viewport; stays in place even when the page scrolls; removed from normal flow |
absolute | Positioned relative to the nearest ancestor that itself has non-static positioning |
sticky | Behaves as relative until a scroll threshold is reached, then behaves as fixed |
.parent {
position: relative;
}
.child {
position: absolute;
top: 0;
right: 0;
}
The float property lets an element “float” to one side of its container while other content wraps around it:
| Value | Effect |
|---|---|
left | Element floats to the left; other content flows around its right side |
right | Element floats to the right |
none | Default — no floating |
inherit | Takes the float value from the parent |
clear is used on an element to prevent it from sitting next to a floated element, forcing it below instead:
.sidebar {
float: left;
width: 30%;
}
.main-content {
float: right;
width: 70%;
}
footer {
clear: both;
}
Demo: Creating the Page Layout
The full two-column layout with a centered, fixed-width page body is built up using the box model and floats:
body {
margin-left: auto;
margin-right: auto;
margin-top: 0;
margin-bottom: 0;
}
#main-header {
background-color: #c87d52;
}
nav {
height: 80px;
width: 1000px;
margin: 0 auto 10px;
}
#main-content {
width: 1000px;
margin-left: auto;
margin-right: auto;
}
#left-menu {
float: left;
width: 30%;
}
#main {
float: right;
width: 70%;
}
footer {
clear: both;
width: 100%;
background-color: #ffd0c9;
padding: 30px;
}
The nav, #main-content (a plain <div> used purely for layout), and #left-menu/#main combination centers a fixed 1000px-wide layout on the page, places the sidebar and main content side by side using float, and clears the footer beneath both columns. A supplementary flexbox-based “sticky footer” technique (wrapping the whole body in a #wrapper using display: flex; flex-direction: column; min-height: 100vh;) is used to keep the footer pinned to the bottom of the viewport even on short pages:
#wrapper {
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 100vh;
}
Using Browser Developer Tools
Browser developer tools (opened with F12, or via the browser menu) let you inspect any live website’s HTML/CSS source. This is a valuable way to learn how real-world sites are structured — the same doctype, html, head, and body elements seen throughout this course appear (in more complex form) on virtually every site on the web. The element-picker tool in developer tools lets you click any visible part of a page and jump straight to the corresponding markup.
Module 6: Working with Images
How Images Work on the Web
An <img> element does not embed image data directly into the HTML file — it references a separate image file, which the browser requests and displays alongside the page. References can be:
- Relative — pointing to a file within the same site (e.g.,
images/logo.png). - Absolute — a full URL pointing to an image hosted elsewhere (a different domain or image-hosting service).
| Format | Colors | Animation | Transparency | Typical use |
|---|---|---|---|---|
| GIF | 256 | Yes | Yes | Simple graphics, memes; rarely used for photos |
| PNG | Many more than GIF | No | Yes | Logos, icons |
| JPG | 16.7 million | No | No | Photographs (compresses well) |
| SVG | Vector (not pixel-based) | N/A | N/A | Logos/icons that must scale to any size without blurring |
Bitmap formats (GIF/PNG/JPG) store a fixed grid of colored pixels, so zooming in eventually blurs the image. SVG stores mathematical descriptions (lines, points, curves) that are recalculated on zoom, so it always renders sharp at any size.
flowchart LR
A[Bitmap: GIF / PNG / JPG] -->|zoom in| B[Pixels enlarge, image blurs]
C[Vector: SVG] -->|zoom in| D[Recalculated, stays sharp]
Using the img Element
<img src="images/products/apple-pie.jpg" alt="Apple pie" width="500" height="300">
| Attribute | Purpose |
|---|---|
src (required) | Path to the image file |
alt (recommended, required for accessibility) | Alternative text shown if the image fails to load, and read aloud by screen readers |
width / height | Desired render size; specifying only one preserves the original aspect ratio; specifying both can distort the image |
width/height only affect display size, not the underlying file size — for performance, images that will always be shown small should be resized ahead of time rather than relying on the browser to scale down a large file.
Demo: Adding Images to the Site
<header id="main-header">
<nav>
<img src="images/logos/bethany-horizontal-logo.png" class="menu-logo">
</nav>
</header>
.menu-logo {
width: 175px;
}
Additional images added to the home page:
<aside id="left-menu">
<a href="index.html"><img src="images/logos/bethany-badge-logo.png" width="150px"></a>
</aside>
<main id="main">
<article>
<img src="images/hero.jpg" width="100%" alt="A delicious blueberry pie">
</article>
</main>
Setting width="100%" scales an image to fill all available horizontal space in its container. An externally hosted image can also be referenced with an absolute URL:
<img src="https://gillcleerenpluralsight.blob.core.windows.net/files/pecan-pie.jpg" width="500" height="300" alt="Pecan Pie">
The figure and figcaption Elements
The semantic <figure> element wraps self-contained content related to the surrounding text (such as a contextual photo or diagram), and can include a <figcaption> to describe it. It still requires the <img> inside it to actually display the image — <figure> only adds semantic meaning:
<figure>
<img src="images/bethany.jpg" width="100%" alt="Bethany in her pie shop">
<figcaption>Bethany from Bethany's Pie Shop</figcaption>
</figure>
Creating a Favicon
A favicon is the small icon shown in a browser tab (and used when a user bookmarks the site). Requirements: PNG, GIF, or ICO format, ideally 32×32 pixels (16×16 also works). It is registered via a <link> element in <head>:
<link rel="icon" type="image/png" href="favicon.png">
Module 7: Creating the Pie Overview Table
Understanding the HTML Table
The <table> element displays tabular data — it should not be used to lay out an entire page (a common practice long ago, now considered outdated since tables are not designed to adapt across device sizes; layout is instead achieved with semantic elements and CSS, as shown in Module 5).
| Table element | Purpose |
|---|---|
<table> | The overall table container |
<tr> | Table row |
<td> | Table data cell (a column within a row) |
<th> | Table header cell (bold by default, semantically marks header content) |
<thead> | Groups the header row(s) |
<tbody> | Groups the main data rows |
<tfoot> | Groups summary/footer rows (e.g., a total) |
<caption> | A caption describing the table’s contents |
Cells can contain anything — text, images, links, or even a nested table. Modern HTML tables retain almost no presentational attributes; the one exception still available is border (an integer), though styling borders, spacing, and sizing through CSS is the recommended approach:
<table border="1">
<tr>
<td><img src="pie.jpg"></td>
<td>Some plain text</td>
</tr>
</table>
Demo: Creating the Overview Page Using a Table
A reusable template.html is first created by copying index.html and stripping out the home-page-specific <article> and <section> content, leaving only the shared header/nav/aside/footer skeleton. pieoverview.html is then built from that template:
<main id="main">
<table border="1">
<tr>
<td><img src="images/products/cheesecakes/cheesecake-small.jpg" width="100"></td>
<td>Cheesecake</td>
<td>Plain cheesecake. Plain pleasure.</td>
<td>$12.95</td>
<td>View details</td>
</tr>
<tr>
<td><img src="images/products/fruitpies/apple-pie-small.jpg" width="100"></td>
<td>Apple pie</td>
<td>Our famous apple pie</td>
<td>$14.95</td>
<td>View details</td>
</tr>
<!-- one row per pie in the catalog -->
</table>
</main>
Once the full list of pies is added, the default double-border look (caused by the border="1" HTML attribute plus default browser cell borders) is replaced with a controlled CSS style:
table, td, th {
border: 1px solid black;
border-collapse: collapse;
padding: 5px;
}
border-collapse: collapse merges adjacent cell borders into a single line instead of showing doubled borders, and padding keeps the cell content from touching the border.
More Table Options: Spanning Rows and Columns
| Attribute | On element | Effect |
|---|---|---|
rowspan="2" | <td> | The cell spans 2 rows vertically (the following row needs one fewer <td>) |
colspan="2" | <td> | The cell spans 2 columns horizontally |
<tr>
<td rowspan="2"><img src="pie.jpg"></td>
<td>Cheesecake</td>
</tr>
<tr>
<td colspan="2">A rich, creamy dessert</td>
</tr>
CSS can also add interactive touches such as row hover highlighting via the :hover pseudo-class:
tr:hover {
background-color: #f5e0da;
}
Demo: Completing the Table with thead, tbody, and caption
<table>
<caption>Current selection of pies</caption>
<thead>
<tr>
<th>Image</th>
<th>Pie name</th>
<th>Description</th>
<th>Price</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<tr>
<td><img src="images/products/cheesecakes/cheesecake-small.jpg" width="100"></td>
<td>Cheesecake</td>
<td>Plain cheesecake. Plain pleasure.</td>
<td>$12.95</td>
<td><a href="cheesecake-detail.html">View details</a></td>
</tr>
<!-- additional rows -->
</tbody>
</table>
table, td, th {
border: 1px solid black;
border-collapse: collapse;
padding: 5px;
}
Because th was not initially included in the selector list alongside table and td, header cells appeared without matching borders until th was added to the rule. Rows in a table do not all need the same number of columns — a summary row can legitimately use colspan to span fewer or more cells than a typical data row.
Module 8: Designing the Pie Detail Page
Understanding Inline and Block Elements
| Type | Behavior |
|---|---|
| Inline | Does not force a new line; takes up only as much horizontal space as its content needs; flows left-to-right until space runs out, then wraps. Examples: <span>, <b>, <a>, <img> |
| Block | Always starts on a new line and stretches to take up available horizontal width. Examples: <h1>–<h6>, <div>, <section>, <p> |
flowchart LR
subgraph Inline flow
i1[inline] --> i2[inline] --> i3[inline, wraps to new line if no space]
end
subgraph Block flow
b1[block] --> b2["block (always new line)"] --> b3["block (always new line)"]
end
Working with the Different Inline Elements
| Inline element | Semantic meaning | Default visual effect |
|---|---|---|
<em> | Emphasis | Italic |
<strong> | Strong importance | Bold |
<mark> | Highlighted/relevant text | Yellow highlight background |
<q> | Short inline quotation | Quotation marks |
<s> | Text no longer correct/relevant | Strikethrough |
<small> | Fine print / side comments | Smaller font size |
<span> | No semantic meaning — generic inline container for styling | None (relies entirely on CSS/inline style) |
<sub> | Subscript | Lowered, smaller text (e.g., chemical formulas) |
<span> is the inline equivalent of <div>: it carries no inherent meaning and exists purely so a specific run of text can be styled or scripted.
Demo: Creating the Pie Detail Page
<div>
<strong>Price: $12.95</strong>
<h2>Description</h2>
<p>
Bethany received the recipe for this delicious apple pie from her grandmother.
At the age of 5, she was already baking this <em>all-American traditional apple</em> pie at her home.
</p>
<p>
This pie is available <strong>every day</strong> and comes in 2 sizes.
<span style="color:#c87d52; font-weight:bold;">
You can serve it cold or warm it for 4 minutes in the microwave to give it an extra touch.
</span>
Your house will smell just like Bethany's Shop!
</p>
<blockquote>
This recipe has been in our family for generations. We hope you enjoy this pie as we do - Bethany
</blockquote>
<p><small>Note that some ingredients may be dangerous if you have certain allergies. See the list of ingredients below.</small></p>
<p><small>Bethany cares about the environment and we try to limit our CO<sub>2</sub> emissions as much as possible.</small></p>
</div>
<blockquote> — unlike the inline elements above — is a block element: it automatically starts on a new line without requiring an explicit line break.
Creating Lists
HTML provides three list-related elements:
| Element | Purpose |
|---|---|
<ol> | Ordered (numbered) list |
<ul> | Unordered (bulleted) list |
<li> | An individual list item, used inside either <ol> or <ul> |
<ol type="A">
<li>First, we combine sugar, flour and spices...</li>
<li>We then create the bottom crust...</li>
<li>Then we beat the egg whites...</li>
<li>The pie goes into the oven at 360° for 35 minutes.</li>
</ol>
<ol start="5">
<li>If something goes wrong, we start all over again!</li>
</ol>
<h3>Ingredients</h3>
<ul>
<li>Butter</li>
<li>Eggs</li>
<li>Sugar</li>
<ul>
<li>Regular sugar</li>
<li>Brown sugar</li>
<li>Caster sugar</li>
</ul>
<li>Flour</li>
<li>Cinnamon</li>
<li>Apples</li>
</ul>
<ol> attribute | Effect |
|---|---|
type="a" | Lowercase letters |
type="A" | Uppercase letters |
type="i" | Lowercase Roman numerals |
start="5" | Numbering continues from 5 instead of restarting at 1 |
Nesting a <ul> (or <ol>) directly inside an <li> creates a sub-list with a different bullet style and extra indentation, useful here for breaking “Sugar” down into its specific varieties.
Including Special Characters
Characters with special meaning in HTML (like < and >) — or characters not easily typed — are written using character entity references, always starting with &, followed by a code, and ending with ;.
| Entity | Renders as |
|---|---|
< | < |
> | > |
© | © |
& | & |
<small>©2023 Bethany's Pie Shop - All rights reserved</small>
Module 9: Adding Navigation to the Site
Working with Links
The <a> (anchor) element creates clickable links, requiring a closing tag and an href attribute (the destination):
<a href="pieoverview.html">All pies</a>
Link content is not limited to text — an image can be made clickable by wrapping it in an <a>:
<a href="index.html"><img src="images/logos/bethany-badge-logo.png"></a>
<a> attribute | Purpose |
|---|---|
href (required) | Destination URL or path |
target="_blank" | Opens the link in a new tab/window |
target="_self" (default) | Opens in the current tab |
title | Extra descriptive text (tooltip on hover, also useful for search engines) |
hreflang | Specifies the language of the target page (rarely used) |
download | Indicates the link should trigger a file download rather than navigation |
rel | Describes the relationship between the current and linked document |
flowchart LR
Index["index.html"] -- "href=pieoverview.html" --> Overview["pieoverview.html"]
Overview -- "href=applepie.html" --> Detail["applepie.html"]
Index -- "href=contact.html" --> Contact["contact.html"]
Index -- "href=about.html" --> About["about.html"]
Demo: Adding Links
Simple relative links between existing pages:
<a href="pieoverview.html">All pies</a>
Turning a set of links into a structured, style-able menu using a list:
<ul class="small-menu">
<li><a href="pieoverview.html">All pies</a></li>
<li><a href="cheesecakes.html">Cheesecakes</a></li>
<li><a href="fruitpies.html">Fruit pies</a></li>
<li><a href="seasonalpies.html">Seasonal pies</a></li>
</ul>
.small-menu {
list-style: none;
text-align: center;
padding: 0;
}
.small-menu > li > a {
text-transform: uppercase;
line-height: 1.5em;
color: #c87d52;
}
Making the logo image clickable and centering it:
<a href="index.html"><img src="images/logos/bethany-badge-logo.png" width="150px"></a>
#left-menu > a > img {
width: 150px;
margin: auto;
display: block;
}
Opening a zoomed image in a new tab:
<a href="images/products/fruitpies/apple-pie.jpg" target="_blank">
<img src="images/products/fruitpies/apple-pie-small.jpg" width="600" alt="Apple pie">
</a>
An absolute link to an external site:
<a href="https://www.example.com">Learn more</a>
Demo: Creating Bookmarks
Links can also jump to a specific location within the current page using an id and a #-prefixed href:
<section id="promos">
<h3>Our weekly promotions</h3>
</section>
<!-- elsewhere on the page -->
<a href="#promos">Promotions</a>
Clicking the bookmark link scrolls/focuses the browser on the element whose id matches the fragment after the #.
Demo: Adding Navigation to the Site
Rather than placing loose <a> tags directly in <nav>, a <ul>-based menu (the common pattern also used by most CSS frameworks) is used and then styled to appear as a horizontal bar:
<nav>
<ul>
<li><img src="images/logos/bethany-horizontal-logo.png" class="menu-logo"></li>
<li><a href="index.html">HOME</a></li>
<li><a href="pieoverview.html">PIES</a></li>
<li><a href="contact.html">CONTACT</a></li>
<li><a href="about.html">ABOUT</a></li>
</ul>
</nav>
nav > ul {
list-style-type: none;
margin: 0;
padding: 0;
padding-top: 10px;
}
nav > ul > li {
float: left;
}
nav > ul > li a {
display: block;
color: white;
text-align: center;
padding: 16px;
text-decoration: none;
font-weight: bold;
}
nav > ul > li a:hover {
color: #ffd0c9;
}
Removing the default list bullets/margin/padding and floating each <li> to the left turns the vertical list into a horizontal navigation bar; the :hover pseudo-class then provides visual feedback.
Different Types of Links
| Link type | href pattern | Behavior |
|---|---|---|
| Relative | pieoverview.html | Points to a page within the same site |
| Absolute | https://example.com/page.html | Points to an external site |
| Download | downloads/pricelist.zip | Points directly to a non-HTML file; clicking downloads it rather than navigating |
mailto:info@bethanyspieshop.com | Opens the visitor’s default email client with the address pre-filled | |
| Bookmark | #promos | Jumps to an element with a matching id on the current page |
<p>Download our full <a href="downloads/Pricelist.zip">price list</a></p>
<p>Contact us via <a href="mailto:info@bethanyspieshop.com">email</a></p>
Module 10: Creating a Contact Form
Understanding How Forms Work
A form lets a visitor send data to the website (as opposed to just reading content). Typical uses include contact forms, shopping-cart quantities, login credentials, and search boxes.
sequenceDiagram
participant Browser
participant Server
Browser->>Browser: User fills in form fields
Browser->>Browser: User clicks Submit
Browser->>Server: POST/GET request with form data
Server->>Server: Server-side script processes data (Node.js, .NET, PHP, Java, etc.)
Server-->>Browser: Response HTML (e.g., confirmation page)
The server-side processing code itself (which might store data in a database, send an email, etc.) is outside the scope of HTML/CSS — HTML is only responsible for capturing and packaging the input.
Creating a Form
<form action="submit.aspx" method="post" id="contactForm">
<label for="firstname">First Name</label>
<input type="text" id="firstname" name="firstname" placeholder="Enter your first name">
<input type="submit" value="Submit">
</form>
<form> attribute | Purpose |
|---|---|
action | URL of the server-side script that will receive/process the submitted data |
method | How data is sent — typically post (in the request body) or get (appended to the URL) |
id | Unique identifier, e.g. for use with JavaScript |
Common <input> type | Renders as |
|---|---|
text | Single-line text box |
submit | Submit button — triggers form submission |
radio | Radio button (mutually exclusive selection when inputs share the same name) |
checkbox | Checkbox (independent tick) |
textarea (separate element, not <input>) | Multi-line text box |
select (separate element, with nested <option>) | Drop-down list |
A <label> linked via for="<input id>" provides an accessible caption for a field.
Demo: Creating the Contact Form
<form action="contact.html" method="post" id="contactForm">
<fieldset>
<legend>Your information</legend>
<label for="firstname">First Name</label>
<input type="text" id="firstname" name="firstname" placeholder="Enter your first name">
<label for="lastname">Last Name</label>
<input type="text" id="lastname" name="lastname" placeholder="Enter your last name">
<label for="country">Country</label>
<select id="country" name="country">
<option value="usa">USA</option>
<option value="belgium">Belgium</option>
<option value="netherlands">Netherlands</option>
</select>
<label for="birthday">Birthday</label>
<input type="date" id="birthday" name="birthday">
<label for="email">Email</label>
<input type="email" id="email" name="email">
</fieldset>
<fieldset>
<legend>The reason you are contacting us</legend>
<label>I have a</label><br>
<input type="radio" id="question" name="contactReason" value="Question" checked>
<label for="question">Question</label><br>
<input type="radio" id="remark" name="contactReason" value="Remark">
<label for="remark">Remark</label><br>
<input type="radio" id="complaint" name="contactReason" value="Complaint">
<label for="complaint">Complaint</label>
<label for="question">Your question</label>
<textarea id="question" name="question" placeholder="Write your text here" style="height:150px"></textarea>
</fieldset>
<input type="submit" value="Submit">
</form>
Radio buttons that share the same name attribute (contactReason) are grouped, so only one of them can be selected at a time; checked pre-selects a default option.
A small inline script (for demonstration purposes, replacing real server-side processing) intercepts the submit event and displays the captured key/value pairs:
<script>
function getData(form) {
var formData = new FormData(form);
var data = '';
for (var pair of formData.entries()) {
data += pair[0] + ": " + pair[1] + "\n";
}
alert(data);
}
document.getElementById("contactForm").addEventListener("submit", function (e) {
e.preventDefault();
getData(e.target);
});
</script>
Opening the browser’s developer tools “Network” tab while submitting a real form shows the exact key/value data package sent to the server.
More Input Types
<label for="country">Country</label>
<select id="country" name="country">
<option value="usa">USA</option>
<option value="belgium">Belgium</option>
</select>
<input type="number" min="1" max="10">
<input type="color">
<input type="range" min="0" max="100">
<input type="date">
<input type="email">
| Input type | Renders as |
|---|---|
select + option | Drop-down list |
number | Numeric-only input (e.g., quantity in a shopping basket) |
color | Color-swatch picker |
range | Slider between a min and max value |
date | Date picker/calendar |
email | Text field with built-in browser validation for email format |
Styling the Form Using CSS
Border properties, used heavily to style form controls:
| Property | Values |
|---|---|
border-color | A color |
border-style | solid, dotted, dashed, double |
border-width | Length (shorthand: one value = all sides, two values = top/bottom then left/right) |
border-radius | Rounds the corners |
The <fieldset> element groups related form controls visually, with <legend> providing its caption:
form {
padding: 25px;
}
input[type=text],
input[type=date],
input[type=email],
select,
textarea {
width: 100%;
padding: 12px;
border: 1px solid #ccc;
border-radius: 8px;
margin-top: 6px;
margin-bottom: 16px;
}
input[type=submit] {
background-color: #c87d52;
color: black;
text-transform: uppercase;
font-size: large;
padding: 12px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
label {
text-transform: uppercase;
font-size: 0.9em;
}
fieldset {
padding: 16px;
border-radius: 10px;
border-color: #c87d52;
margin-bottom: 30px;
}
legend {
margin-left: 16px;
text-transform: uppercase;
}
The attribute selector (input[type=text]) targets only text-type inputs, letting other input types (like radio buttons and checkboxes, which should not stretch to 100% width) keep their native appearance.
Module 11: Including External Content Using iframe
Introducing the iframe
An <iframe> embeds another page — often hosted externally, such as a video platform — directly inside the current page, instead of linking away to it. This keeps visitors on the site rather than sending them elsewhere.
<iframe width="560" height="315" src="https://example.com/embed/VIDEO_ID"
title="Video player" frameborder="0" allowfullscreen></iframe>
Because the embedded content is not under the host site’s control, iframes can introduce security concerns. The sandbox attribute restricts what the embedded page is allowed to do:
<!-- Maximum restriction: no scripts, no forms, no same-origin access -->
<iframe src="https://example.com" sandbox></iframe>
<!-- Relaxed slightly: allow forms to be submitted from within the iframe -->
<iframe src="https://example.com" sandbox="allow-forms"></iframe>
Demo: Adding the iframe
<main id="main">
<article>
<header>
<h1>About Bethany's Pie Shop</h1>
</header>
<p>In the video below, you can see how we create our pies.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/VIDEO_ID"
title="YouTube video player" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen></iframe>
</article>
</main>
The result is a video player embedded directly in the “About” page, keeping visitors on the site instead of redirecting them to an external video platform.
Module 12: Storing Data with JavaScript
Introducing Browser APIs
Beyond static markup and styling, browsers expose numerous built-in APIs (Application Programming Interfaces) that JavaScript code can call to add rich, interactive behavior — well over 100 such APIs exist as part of the modern web platform.
| API | Purpose |
|---|---|
| Geolocation API | Determine the physical location of the user’s device (with consent) |
Storage API (localStorage / sessionStorage) | Persist key/value data on the user’s machine |
| Audio/Video API | Programmatically control media playback |
| Drag and Drop API | Let users drag elements around the page |
flowchart LR
HTML[HTML markup] --> Behavior["Interactive behavior needed?"]
Behavior -- No --> CSSOnly[Style with CSS only]
Behavior -- Yes --> JS[Write JavaScript]
JS --> API["Call a built-in Browser API (e.g., Storage API)"]
Working with Local Storage
Cookies were the traditional way to persist small amounts of data (max ~4KB) on the client, sent back to the server with every request. The Web Storage API offers a JavaScript-only alternative for storing larger amounts of key/value data purely in the browser, with two flavors:
| Storage type | Lifetime |
|---|---|
localStorage | Persists indefinitely, even after the browser is closed, until explicitly cleared |
sessionStorage | Cleared automatically when the browser tab/window is closed |
// Store a value
window.localStorage.setItem('applepie', true);
// Retrieve a value
var isFavorite = window.localStorage.getItem('applepie');
// Remove a single value
window.localStorage.removeItem('applepie');
// Number of stored items
var count = window.localStorage.length;
// Clear everything
window.localStorage.clear();
Demo: Saving a Favorite to Local Storage
A “Save as favorite” button is added to the pie detail page, invoking a JavaScript function on click:
<button type="button" onclick="save()">Save as favorite</button>
<span id="favorite"></span>
<script>
function save() {
localStorage.setItem("applepie", true);
}
window.onload = function () {
var favorite = document.getElementById('favorite');
if (window.localStorage) {
var storage = window.localStorage;
if (storage.getItem('applepie') == 'true') {
favorite.innerHTML = "Apple pie is one of your favorites";
}
}
}
</script>
sequenceDiagram
participant User
participant Button
participant localStorage
participant Page
User->>Button: Click "Save as favorite"
Button->>localStorage: setItem("applepie", true)
User->>Page: Refresh page
Page->>Page: window.onload fires
Page->>localStorage: getItem("applepie")
localStorage-->>Page: "true"
Page->>Page: Update #favorite span text
Clicking the button stores the flag, but the visible confirmation text only appears after the page reloads and window.onload re-checks storage — demonstrating that localStorage truly persists across page loads (and even full browser restarts), unlike a variable held only in memory. The stored value can also be inspected directly in the browser’s developer tools, under the Application/Storage panel.
Summary
Across this course, a complete website — Bethany’s Pie Shop — was built from an empty file to a fully working, multi-page, styled site with basic client-side interactivity. The core principles established along the way:
- HTML defines structure and meaning, never appearance. Elements should be chosen based on what content is (a heading, a list, a table of data, a semantic section) rather than how it should look.
- CSS defines appearance, separated into its own file(s) and linked from the HTML
<head>, enabling consistent, centrally-updatable styling across every page of a site. - Selectors and the cascade (element, ID, class, attribute, and combinator selectors, plus pseudo-classes) give precise control over exactly which elements a style rule affects.
- Semantic elements (
header,nav,main,article,section,aside,footer) replace meaningless<div>soup wherever a true semantic role exists, improving accessibility and maintainability;<div>/<span>remain the correct tools for purely presentational grouping. - The box model (content, padding, border, margin) together with
positionandfloatis the foundation of arranging content on the page. - Tables are for tabular data only, never full-page layout.
- Inline vs. block behavior determines whether an element starts a new line or flows with surrounding content.
- Links (
<a>) — relative, absolute, download, email (mailto:), and bookmark (#id) — connect pages (and locations within pages) into a real, navigable site. - Forms capture user input and package it for a server to process; HTML supplies the structure and built-in input types, while the actual data handling happens server-side.
<iframe>embeds external content directly into a page, with thesandboxattribute available to constrain what embedded content can do.- Browser APIs, accessed through JavaScript (e.g., the Storage API’s
localStorage), let a page persist information on the user’s machine and add interactivity beyond what static HTML/CSS can achieve alone.
Quick-reference: Core Elements Used in This Course
| Category | Elements |
|---|---|
| Document skeleton | <!DOCTYPE html>, <html>, <head>, <body>, <meta>, <title>, <link> |
| Text content | <h1>–<h6>, <p>, <br>, <hr>, <b>, <i>, <em>, <strong>, <mark>, <q>, <s>, <small>, <span>, <sub>, <blockquote> |
| Semantic structure | <header>, <nav>, <main>, <article>, <section>, <aside>, <footer>, <address>, <div> |
| Media | <img>, <figure>, <figcaption> |
| Tables | <table>, <tr>, <td>, <th>, <thead>, <tbody>, <tfoot>, <caption> |
| Lists | <ol>, <ul>, <li> |
| Links | <a> |
| Forms | <form>, <label>, <input>, <select>, <option>, <textarea>, <fieldset>, <legend> |
| Embedding | <iframe> |
| Scripting | <script> |
Quick-reference: Core CSS Concepts
| Concept | Key properties/syntax |
|---|---|
| Selectors | element, #id, .class, [attribute], *, descendant (space), child (>), grouping (,), pseudo-class (:hover) |
| Text styling | color, font-size, font-weight, font-family, text-align, text-decoration, text-transform, font-variant, line-height |
| Box model | padding, margin, border, border-radius, box-sizing |
| Positioning | position (static/relative/fixed/absolute/sticky), top/right/bottom/left, float, clear |
| Custom fonts | @font-face, font-family |
Completion Checklist
- Understand the difference between HTML (structure/content) and CSS (appearance).
- Create a valid HTML page skeleton (
DOCTYPE,html,head,body). - Use headings, paragraphs, and inline text elements appropriately and semantically.
- Link an external stylesheet and write element/ID/class/attribute selectors.
- Build a semantic page layout using
header,nav,main,article,section,aside,footer. - Apply the box model and
position/floatto arrange page regions. - Add images with proper
alttext, and usefigure/figcaptionfor contextual images. - Build a data table with
thead/tbody/caption, and userowspan/colspanwhen needed. - Create ordered and unordered lists, including nested lists.
- Link pages together (relative, absolute, download,
mailto:, and bookmark links). - Build and style a contact form with appropriate input types and
fieldset/legendgrouping. - Embed external content safely using
iframeand thesandboxattribute. - Use a browser Storage API (
localStorage) to persist simple data between page visits.
Search Terms
html · css · websites · web · fundamentals · frontend · development · page · elements · form · images · iframe · introducing · links · pie · site · text · browser · contact · content · core · detail · environment · headings