Beginner

HTML & CSS: Building Websites

html · css · websites · web · fundamentals · frontend · development · page · elements · form · images · iframe · introducing · links · pie · site · text · browser · contact · content · co...

Table of Contents

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:

ToolPurpose
A text editor (Visual Studio Code used throughout)Writing HTML, CSS, and JavaScript code
A “Live Server” style editor extensionAutomatically serves and refreshes the page in the browser on every save
A web browserViewing 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

  1. Install a code editor (e.g., Visual Studio Code).
  2. Open the editor’s extension marketplace and search for a live-reloading server extension (e.g., “Live Server”), then install it.
  3. 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 .htm or .html extension 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>
PartPurpose
<!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

  1. Create a project folder (e.g., BethanysPieShop) and open it in the editor — most editors organize work around folders.
  2. Create a new file named index.html. index is the conventional default filename served by web servers when a site’s root URL is requested (e.g., bethanyspieshop.com serves index.html automatically).
  3. Add the doctype, then the html element, then head and body:
<!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>
  1. 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>
HeadingTypical 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/AttributeEffect
<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 headingsLegacy 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>&copy;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;
}
PartMeaning
.headerThe 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 typeSyntaxMatches
Element selectorh2 { }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 selectora[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 combinatordiv > .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-classa: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:

PropertyPurposeExample
font-sizeText size — prefer relative units (em) over pixels for better scaling across devicesfont-size: 1.5em;
colorText color (not font-color)color: #c87d52;
font-weightBoldnessfont-weight: bold;
text-decorationUnderline, etc.text-decoration: underline;
text-transformUpper/lowercase transformtext-transform: uppercase;
font-variantSmall capsfont-variant: small-caps;
text-alignHorizontal alignmenttext-align: center;
line-heightVertical spacing between linesline-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., adding font-weight: bold specifically to h2).
  • 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 em values 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:

CategoryPurposeExample elements
Metadata contentPresentation/behavior of the document, or links to external resources<link>, <style>, <title>
Flow contentMost elements used in the document bodyimages, links, sections, paragraphs
Sectioning contentOrganizes the page into logical regions<article>, <section>, <nav>, <aside>
Phrasing contentElements related to text<span>, <em>, <strong>
Heading contentDefines headers<h1><h6>
Embedded contentBrings in external resources<img>, <video>, <iframe>
Interactive contentElements 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 elementRole
<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
PartDescription
ContentThe actual text/image/element
PaddingSpace between the content and the border (inside the border)
BorderA visible or invisible outline around the padding+content
MarginSpace 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 givenMeaning
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:

ValueBehavior
static (default)Normal document flow; top/left/etc. have no effect
relativePositioned relative to where it would normally sit, using top/left/right/bottom, without affecting sibling elements
fixedPositioned relative to the browser viewport; stays in place even when the page scrolls; removed from normal flow
absolutePositioned relative to the nearest ancestor that itself has non-static positioning
stickyBehaves 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:

ValueEffect
leftElement floats to the left; other content flows around its right side
rightElement floats to the right
noneDefault — no floating
inheritTakes 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).
FormatColorsAnimationTransparencyTypical use
GIF256YesYesSimple graphics, memes; rarely used for photos
PNGMany more than GIFNoYesLogos, icons
JPG16.7 millionNoNoPhotographs (compresses well)
SVGVector (not pixel-based)N/AN/ALogos/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">
AttributePurpose
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 / heightDesired 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 elementPurpose
<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

AttributeOn elementEffect
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

TypeBehavior
InlineDoes 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>
BlockAlways 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 elementSemantic meaningDefault visual effect
<em>EmphasisItalic
<strong>Strong importanceBold
<mark>Highlighted/relevant textYellow highlight background
<q>Short inline quotationQuotation marks
<s>Text no longer correct/relevantStrikethrough
<small>Fine print / side commentsSmaller font size
<span>No semantic meaning — generic inline container for stylingNone (relies entirely on CSS/inline style)
<sub>SubscriptLowered, 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:

ElementPurpose
<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> attributeEffect
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 ;.

EntityRenders as
&lt;<
&gt;>
&copy;©
&amp;&
<small>&copy;2023 Bethany's Pie Shop - All rights reserved</small>

Module 9: Adding Navigation to the Site

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> attributePurpose
href (required)Destination URL or path
target="_blank"Opens the link in a new tab/window
target="_self" (default)Opens in the current tab
titleExtra descriptive text (tooltip on hover, also useful for search engines)
hreflangSpecifies the language of the target page (rarely used)
downloadIndicates the link should trigger a file download rather than navigation
relDescribes 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"]

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.

Link typehref patternBehavior
Relativepieoverview.htmlPoints to a page within the same site
Absolutehttps://example.com/page.htmlPoints to an external site
Downloaddownloads/pricelist.zipPoints directly to a non-HTML file; clicking downloads it rather than navigating
Emailmailto:info@bethanyspieshop.comOpens the visitor’s default email client with the address pre-filled
Bookmark#promosJumps 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> attributePurpose
actionURL of the server-side script that will receive/process the submitted data
methodHow data is sent — typically post (in the request body) or get (appended to the URL)
idUnique identifier, e.g. for use with JavaScript
Common <input> typeRenders as
textSingle-line text box
submitSubmit button — triggers form submission
radioRadio button (mutually exclusive selection when inputs share the same name)
checkboxCheckbox (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 typeRenders as
select + optionDrop-down list
numberNumeric-only input (e.g., quantity in a shopping basket)
colorColor-swatch picker
rangeSlider between a min and max value
dateDate picker/calendar
emailText field with built-in browser validation for email format

Styling the Form Using CSS

Border properties, used heavily to style form controls:

PropertyValues
border-colorA color
border-stylesolid, dotted, dashed, double
border-widthLength (shorthand: one value = all sides, two values = top/bottom then left/right)
border-radiusRounds 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.

APIPurpose
Geolocation APIDetermine 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 APIProgrammatically control media playback
Drag and Drop APILet 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 typeLifetime
localStoragePersists indefinitely, even after the browser is closed, until explicitly cleared
sessionStorageCleared 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 position and float is 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 the sandbox attribute 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

CategoryElements
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

ConceptKey properties/syntax
Selectorselement, #id, .class, [attribute], *, descendant (space), child (>), grouping (,), pseudo-class (:hover)
Text stylingcolor, font-size, font-weight, font-family, text-align, text-decoration, text-transform, font-variant, line-height
Box modelpadding, margin, border, border-radius, box-sizing
Positioningposition (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/float to arrange page regions.
  • Add images with proper alt text, and use figure/figcaption for contextual images.
  • Build a data table with thead/tbody/caption, and use rowspan/colspan when 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/legend grouping.
  • Embed external content safely using iframe and the sandbox attribute.
  • 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

Interested in this course?

Contact us to book it or get a custom training plan for your team.