Frontend Development using React.js or Angular

Chapter 2
HTML, CSS, Responsive Design, and JavaScript Essentials

Diploma in Full Stack Programming
Frontend Development using React.js or Angular   •   Diploma

HTML, CSS, Responsive Design, and JavaScript Essentials

Introduction and Context

Modern React and Angular applications are built on web-platform fundamentals. Frameworks provide components, routing, state-management patterns, and development tools, but they do not replace HTML, CSS, or JavaScript. A developer who understands the platform can produce interfaces that are semantic, accessible, responsive, maintainable, and easier to debug.

This chapter develops the foundation required for later component-based development. It treats the browser as a runtime that parses HTML into a document tree, applies CSS rules to calculate presentation, and executes JavaScript to respond to events and change the document. TypeScript extends JavaScript with static analysis and is widely used in Angular and many React projects.

Learning Objectives

By the end of this chapter, you should be able to:

  1. Construct semantic HTML documents using appropriate structural and interactive elements.
  2. Apply modern CSS selectors, the box model, layout systems, and reusable design rules.
  3. Design responsive interfaces using fluid measurements, Flexbox, Grid, and media queries.
  4. Explain core JavaScript and TypeScript syntax, scope, objects, arrays, and modules.
  5. Register and handle browser events safely.
  6. Use promises and async/await for asynchronous operations.
  7. Apply validation, error handling, accessibility, and maintainability practices.

1. The Browser Programming Model

A browser processes a web page through several related structures. HTML is parsed into the Document Object Model (DOM), a tree of elements and text nodes. CSS is parsed into the CSS Object Model (CSSOM). The browser combines these structures to calculate styles, layout, painting, and compositing. JavaScript can inspect and modify the DOM and CSS-related state, although excessive modification can reduce performance.

A useful conceptual model is:

  • HTML: content and meaning.
  • CSS: visual presentation and layout.
  • JavaScript: behaviour, interaction, and application logic.
  • Browser APIs: capabilities such as fetch, timers, storage, and location.

The model is not strictly a sequence in every situation. Loading, style calculation, painting, and script execution may interact. Nevertheless, separating concerns helps developers determine whether a problem is semantic, visual, behavioural, or environmental.

2. Semantic HTML

HTML elements communicate the role of content. Semantic markup improves accessibility, search-engine interpretation, maintainability, and the quality of the DOM used by assistive technologies.

A basic document includes a document type declaration, language metadata, character encoding, a title, and visible body content:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Course Dashboard</title>
  </head>
  <body>
    <header>
      <h1>Course Dashboard</h1>
      <nav aria-label="Primary navigation">
        <a href="/courses">Courses</a>
        <a href="/profile">Profile</a>
      </nav>
    </header>
    <main>
      <section aria-labelledby="progress-heading">
        <h2 id="progress-heading">Learning progress</h2>
        <p>You have completed three modules.</p>
      </section>
    </main>
    <footer>© 2025 Learning Platform</footer>
  </body>
</html>

2.1 Structural and content elements

Common semantic elements include header, nav, main, section, article, aside, and footer. A section normally represents a thematic grouping and should generally have a heading. An article represents a self-contained item that could be distributed independently, such as a news item or forum post. div remains useful as a generic container when no more meaningful element applies; it should not replace semantic elements merely because it is convenient.

Headings should express hierarchy rather than visual size. A page normally has one primary h1, followed by appropriately nested subheadings. Visual appearance should be controlled with CSS instead of selecting a heading level for its default font size.

2.2 Links, buttons, and forms

An anchor (a) represents navigation to a resource. A button represents an action in the current interface, such as opening a dialog or submitting a form. Replacing either with a styled div creates accessibility and keyboard problems.

Forms should associate labels with controls and should use suitable input types:

<form id="enrolment-form">
  <label for="email">Email address</label>
  <input id="email" name="email" type="email" required autocomplete="email">
  <button type="submit">Enrol</button>
</form>

The name attribute identifies submitted data. Native validation such as required and type="email" is useful, but server-side validation remains necessary because client-side controls can be bypassed.

Images require meaningful alternative text. If an image conveys information, describe that information in alt. If it is purely decorative, use alt="". A complex image may require a nearby textual explanation rather than an excessively long alternative-text value.

3. CSS Foundations

CSS rules consist of selectors and declarations:

.card {
  padding: 1rem;
  border: 1px solid #d1d5db;
  background: white;
}

The cascade determines which declarations apply. In simplified terms, the browser considers origin and importance, selector specificity, and source order. A narrowly targeted rule does not automatically make a design better; excessive specificity makes later maintenance difficult. Prefer classes, predictable naming, and component-level styles over deeply nested selectors and IDs.

3.1 The box model

Every ordinary element is represented by content, padding, border, and margin. With the default content-box model, declared width applies to the content area, so padding and borders increase the rendered outer size. A widely used reset is:

*, *::before, *::after {
  box-sizing: border-box;
}

With border-box, the declared width includes content, padding, and border. This makes layouts easier to reason about, although the box can still exceed its containing block if margins or other constraints are added.

Spacing should be systematic. CSS custom properties can express a small design system:

:root {
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-4: 1rem;
  --color-primary: #155eef;
}

Custom properties inherit by default and can be overridden in a component or theme. They are values, not compile-time constants, so they can be changed dynamically.

3.2 Selectors and states

Useful selectors include class selectors (.notice), attribute selectors (input[required]), descendant selectors (.menu a), child selectors (.menu > li), and pseudo-classes such as :hover, :focus-visible, :checked, and :disabled. Pseudo-elements such as ::before and ::after generate presentational content; they should not be the only location of essential information.

Keyboard focus must remain visible. A robust rule is preferable to removing the outline:

:focus-visible {
  outline: 3px solid #f59e0b;
  outline-offset: 3px;
}

Colour contrast, text resizing, motion preferences, and focus order should be considered as accessibility requirements rather than optional decoration.

4. Responsive Design

Responsive design allows one interface to adapt to different viewport sizes, input methods, and user preferences. It is not simply a collection of device-specific breakpoints. A strong approach begins with a flexible layout and introduces a breakpoint when the content requires one.

4.1 Fluid foundations

Use relative units such as rem, %, fr, vw, and clamp() where appropriate. A common readable-content pattern is:

.page {
  width: min(100% - 2rem, 70rem);
  margin-inline: auto;
}

h1 {
  font-size: clamp(1.75rem, 4vw, 3rem);
}

The min() expression selects the smaller permitted value. clamp(minimum, preferred, maximum) keeps a value within bounds. Relative units do not eliminate the need for testing: long translations, zoom, and large text can expose overflow.

4.2 Flexbox

Flexbox is primarily a one-dimensional layout system. It distributes items along a main axis and aligns them along a cross axis.

.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

flex-wrap allows items to move onto additional lines. gap creates consistent spacing without fragile margin combinations. The axes depend on flex-direction; they are not always horizontal and vertical.

4.3 CSS Grid

Grid is useful for two-dimensional relationships between rows and columns:

.course-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1rem;
}

minmax(16rem, 1fr) gives each track a minimum size while allowing available space to be shared. auto-fit collapses empty tracks, allowing cards to expand. This pattern often reduces the need for multiple breakpoints.

A media query can adjust layout when evidence shows that a change improves usability:

@media (min-width: 48rem) {
  .layout {
    display: grid;
    grid-template-columns: 16rem 1fr;
    gap: 2rem;
  }
}

Responsive testing should include narrow and wide viewports, keyboard-only use, zoom, touch targets, slow connections, and real content rather than placeholder text.

5. JavaScript Essentials

JavaScript is dynamically typed: values have types, while variables do not permanently have one type. Prefer const when a binding is not reassigned and let when reassignment is required. Avoid var in modern application code because its function-scoped behaviour and hoisting rules can obscure intent.

Primitive values include strings, numbers, bigints, booleans, undefined, null, and symbols. Objects, arrays, and functions are reference values. Strict equality (===) avoids many implicit-conversion surprises.

const price = 25;
const quantity = 3;
const total = price * quantity;

if (total > 50) {
  console.log("Eligible for a discount");
}

Template literals support readable interpolation:

const learner = "Amina";
const message = `Welcome, ${learner}.`;

Arrays support methods such as map, filter, find, some, and reduce. Use the method whose meaning matches the operation. map creates a transformed array, filter retains matching values, and find returns the first matching value or undefined.

const courses = [
  { title: "HTML", complete: true },
  { title: "JavaScript", complete: false }
];

const completedTitles = courses
  .filter(course => course.complete)
  .map(course => course.title);

Objects group related data. Destructuring and spread syntax can improve clarity:

const user = { id: 7, name: "Amina", role: "student" };
const { name, ...metadata } = user;
const updatedUser = { ...user, role: "mentor" };

Spread creates a shallow copy. Nested objects remain shared references, so it is not a universal deep-copy operation.

5.1 Scope and functions

let and const are block-scoped. Functions create their own scope, and closures allow a function to retain access to variables from its defining environment. Closures are useful for encapsulation but can retain data longer than intended if registered callbacks are never removed.

Arrow functions have lexical this; they do not create their own this binding. This makes them useful for callbacks but not interchangeable with every method or constructor function.

5.2 TypeScript

TypeScript adds static type checking and compiles to JavaScript. Types document contracts and reveal many mistakes before execution:

type Course = {
  id: number;
  title: string;
  completed: boolean;
};

function completionLabel(course: Course): string {
  return course.completed ? "Complete" : "In progress";
}

Interfaces and type aliases can describe object shapes. Union types express a controlled set of alternatives:

type RequestState =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: Course[] }
  | { status: "error"; message: string };

TypeScript checks compile-time assumptions; it does not validate untrusted runtime data automatically. Data received from an API should still be validated and handled defensively.

6. Modules and Application Structure

Modules divide code into files with explicit imports and exports. Explicit dependencies improve testing and reduce hidden coupling.

// format.js
export function formatDate(date) {
  return new Intl.DateTimeFormat("en", { dateStyle: "medium" }).format(date);
}

// dashboard.js
import { formatDate } from "./format.js";
console.log(formatDate(new Date()));

A module has its own scope. Named exports are useful when a file provides several related functions; a default export can represent one primary value. Avoid circular dependencies and excessively large modules. In React and Angular, component files commonly import services, types, styles, and utility functions through this module system.

7. Events and the DOM

An event represents something that occurs, such as a click, key press, input change, or form submission. addEventListener is preferred to inline event attributes:

const form = document.querySelector("#enrolment-form");

form.addEventListener("submit", event => {
  event.preventDefault();
  const data = new FormData(form);
  console.log(data.get("email"));
});

The event object supplies information about the event and its target. Events can bubble from a target through its ancestors. Event delegation uses this behaviour to handle many similar descendants with one listener:

const list = document.querySelector(".course-list");

list.addEventListener("click", event => {
  const button = event.target.closest("button[data-course-id]");
  if (!button || !list.contains(button)) return;
  console.log(button.dataset.courseId);
});

Be careful with dynamically inserted HTML. textContent treats data as text, whereas innerHTML parses a string as markup and can create cross-site scripting risks when the string contains untrusted input. Use framework escaping and established sanitisation tools where HTML must be accepted.

8. Asynchronous Programming

JavaScript executes code on an event loop. A long-running synchronous operation blocks interaction. Browser APIs perform tasks such as network requests outside the main execution flow and later schedule callbacks or promise reactions.

A promise represents the eventual result of an asynchronous operation. It may be pending, fulfilled, or rejected. fetch returns a promise, but it rejects primarily for network-level failures; an HTTP 404 or 500 normally still produces a response. Therefore, check response.ok explicitly:

async function loadCourses() {
  const response = await fetch("/api/courses");
  if (!response.ok) {
    throw new Error(`Request failed: ${response.status}`);
  }
  return response.json();
}

async function showCourses() {
  try {
    const courses = await loadCourses();
    renderCourses(courses);
  } catch (error) {
    console.error(error);
    showError("Courses could not be loaded.");
  }
}

async functions always return promises. await pauses that function until the promise settles; it does not block the entire browser thread. Independent requests can run concurrently:

const [courses, profile] = await Promise.all([
  loadCourses(),
  loadProfile()
]);

Promise.all rejects when any input promise rejects. Use Promise.allSettled when every result should be inspected independently. Avoid unhandled rejections, and provide cancellation where appropriate with AbortController:

const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000);

try {
  const response = await fetch("/api/courses", { signal: controller.signal });
  if (!response.ok) throw new Error("Server error");
} catch (error) {
  if (error.name === "AbortError") {
    console.log("Request cancelled");
  } else {
    console.error(error);
  }
} finally {
  clearTimeout(timeout);
}

9. Error Handling and Quality

Errors should be classified and communicated appropriately. A validation error may be corrected by the user; an authentication error may require sign-in; a server error may require retrying; a programming error requires developer investigation. Do not expose stack traces, tokens, or internal system details in user-facing messages.

Good error handling includes:

  • validating inputs at trust boundaries;
  • checking network and response status;
  • logging useful diagnostic context without personal or secret data;
  • presenting an understandable recovery action;
  • preserving accessibility by associating errors with controls;
  • avoiding silent failure.

Use finally for cleanup that must occur whether an operation succeeds or fails. Throw Error objects rather than arbitrary strings. Error handling does not mean catching everything at the lowest level; catch an error where the application can recover or provide meaningful context.

10. Accessibility, Security, and Professional Practice

Semantic HTML, keyboard access, visible focus, sufficient contrast, meaningful labels, and correct status announcements are core professional responsibilities. The Web Content Accessibility Guidelines provide a widely used framework, but accessibility should be tested with keyboard navigation, screen readers where possible, browser zoom, and automated tools.

Security principles include output encoding, secure transport, careful dependency management, and least privilege. Never place private credentials in client-side code. Treat browser input, URL parameters, local storage, and API responses as untrusted. Use Content Security Policy and appropriate cookie settings as part of a broader security strategy.

Performance is also a user-experience concern. Reduce unnecessary JavaScript, split large bundles, optimise images, avoid layout thrashing, and measure using browser developer tools and field-oriented metrics. A framework cannot compensate for an inefficient algorithm or an inaccessible interaction model.

Applied Case: Illustrative Course Dashboard

Background

A training provider wants a dashboard showing a learner's courses, progress, and upcoming assessments. The initial prototype uses div elements for all content, fixed widths, click handlers attached to each card, and a single unhandled API request.

Problem

On mobile screens, course cards overflow. Keyboard users cannot activate the cards consistently. A failed request leaves a blank page, and the interface announces no loading or error state.

Analysis

The defects have different causes. The markup lacks semantic roles and native controls; the CSS assumes a desktop width; event handling is tightly coupled to generated elements; and asynchronous control flow has no explicit state model.

Approach and Solution

The team replaces the layout with header, main, section, headings, links, and buttons where actions are required. A Grid rule uses repeat(auto-fit, minmax(16rem, 1fr)). Cards use real buttons for actions and retain visible focus. A single delegated listener handles card actions. The data layer checks response.ok, uses AbortController, and exposes loading, success, and error states. TypeScript defines the Course and RequestState contracts. Error text is connected to the relevant region with an accessible status mechanism.

Outcome

The interface works at narrow and wide widths, can be operated without a mouse, and gives users feedback during loading and failure. The explicit state model also makes the code easier to test and prepares the dashboard for conversion into React or Angular components.

Learning Points

The case demonstrates that framework readiness depends on platform knowledge. Semantic HTML reduces custom behaviour, responsive CSS reduces device-specific code, typed data models clarify application states, and disciplined asynchronous handling improves reliability.

Contemporary Development Considerations

Modern frontend development increasingly combines component frameworks with native platform capabilities. Web Components, CSS nesting, container queries, logical properties, and accessible design-system primitives can reduce duplication when supported by the project’s browser policy. TypeScript adoption continues because it improves refactoring and editor feedback, but types must be complemented by runtime validation.

AI-assisted coding can help generate tests, explain unfamiliar APIs, or suggest refactors. It can also produce insecure, inaccessible, or syntactically plausible but incorrect code. Professional developers must review generated code, verify dependencies, protect confidential information, and test behaviour. AI assistance changes the speed of implementation; it does not transfer responsibility for correctness, licensing, privacy, or security.

Chapter Summary

HTML provides document structure and meaning; semantic elements improve accessibility and maintainability. CSS controls presentation through the cascade, selectors, the box model, custom properties, Flexbox, Grid, and responsive rules. Responsive design should begin with fluid constraints and content-driven breakpoints rather than assumptions about particular devices.

JavaScript supplies variables, functions, objects, arrays, modules, DOM access, and event handling. TypeScript adds compile-time contracts but does not replace runtime validation. Promises and async/await support non-blocking asynchronous work, while status checks, cancellation, try/catch, and finally make network operations reliable. Professional frontend development also requires accessibility, secure handling of untrusted data, performance measurement, clear error communication, and critical review of tools, including AI-assisted tools.

Key Terms

Semantic HTML: Markup whose elements communicate the role and meaning of content.

DOM: The browser’s object representation of an HTML document.

CSS cascade: The process used to resolve competing style declarations.

Specificity: The selector-priority mechanism used in the cascade.

Box model: The relationship among content, padding, border, and margin.

Flexbox: A one-dimensional CSS layout system.

CSS Grid: A two-dimensional CSS layout system.

Responsive design: Designing interfaces that adapt to viewport and user conditions.

Breakpoint: A condition at which layout or presentation rules change.

Accessibility: The practice of making technology usable by people with diverse abilities.

Closure: A function retaining access to variables from its defining scope.

Module: A file with its own scope and explicit imports and exports.

Event bubbling: The movement of an event from a target through ancestor elements.

Promise: An object representing the eventual result of asynchronous work.

async/await: Syntax for writing promise-based operations in sequential style.

TypeScript: A statically checked superset of JavaScript that compiles to JavaScript.

Event delegation: Handling descendant events through a common ancestor listener.

Runtime validation: Checking actual data values while a program executes.

Cross-site scripting: Injection of executable content into a web context.

Review Questions

Short Answer Questions

  1. What is the difference between an anchor and a button in HTML?
  2. Explain the purpose of box-sizing: border-box.
  3. What problem does response.ok solve when using fetch?
  4. What is event delegation?
  5. Why does TypeScript not eliminate the need for runtime validation?

Long Answer Questions

  1. Explain how semantic HTML, accessibility, and maintainability are related. Include examples of suitable elements.
  2. Compare Flexbox and CSS Grid and describe a situation in which each is appropriate.
  3. Describe the CSS cascade, including specificity and source order, and recommend practices for keeping styles maintainable.
  4. Explain promises, async/await, Promise.all, and error handling in a network-based application.
  5. Discuss how a frontend developer should address security, performance, and accessibility in a production interface.

Analytical / Application Questions

  1. A dashboard displays four cards in a fixed 1,200-pixel container and overflows on phones. Propose an HTML and CSS redesign using semantic structure, Grid or Flexbox, and fluid constraints. Justify each decision.
  2. An API returns a 500 response, but the application displays an empty course list. Analyse the likely asynchronous error and design a state model for loading, success, empty, and failure conditions.
  3. A clickable div works with a mouse but not reliably with a keyboard or screen reader. Explain the defects and propose a standards-based replacement and testing strategy.

Multiple Choice Questions with Answers

Question 1

Which HTML element is most appropriate for a control that performs an action without navigating to another resource?

A. button

B. a

C. div

D. span

Correct Answer: A. button

Question 2

What does box-sizing: border-box cause the declared width to include?

A. Only the content area

B. Content, padding, and border

C. Content and margin only

D. Margin and border only

Correct Answer: B. Content, padding, and border

Question 3

Which CSS layout system is primarily one-dimensional?

A. CSS Grid

B. Flexbox

C. Float layout

D. Positioning

Correct Answer: B. Flexbox

Question 4

What does the filter array method return?

A. The first matching element

B. A Boolean indicating whether any element matches

C. A new array containing elements that satisfy a condition

D. A string containing all matching elements

Correct Answer: C. A new array containing elements that satisfy a condition

Question 5

What is a key characteristic of an async function?

A. It can never throw an error

B. It always returns a promise

C. It blocks the entire browser thread

D. It can only call synchronous functions

Correct Answer: B. It always returns a promise

Question 6

Why should code using fetch usually check response.ok?

A. fetch rejects every HTTP error automatically

B. response.ok parses JSON automatically

C. HTTP error responses generally still produce a fulfilled fetch promise

D. response.ok cancels slow requests

Correct Answer: C. HTTP error responses generally still produce a fulfilled fetch promise

Question 7

Which practice reduces cross-site scripting risk when displaying untrusted text?

A. Assigning it with textContent

B. Assigning it directly to innerHTML

C. Using an inline event handler

D. Disabling all browser validation

Correct Answer: A. Assigning it with textContent

Question 8

What is the principal purpose of a JavaScript module?

A. To remove all asynchronous operations

B. To provide explicit imports, exports, and file-level scope

C. To make every variable global

D. To replace HTML semantics

Correct Answer: B. To provide explicit imports, exports, and file-level scope

Question 9

What happens when Promise.all receives several promises and one rejects?

A. It fulfils with only the successful values

B. It waits forever

C. The combined promise rejects

D. It converts the rejection into undefined

Correct Answer: C. The combined promise rejects

Question 10

Why is a native button preferable to a clickable div for an action?

A. Buttons cannot receive focus

B. Buttons provide established keyboard and accessibility behaviour

C. Buttons always require JavaScript

D. Buttons cannot be styled with CSS

Correct Answer: B. Buttons provide established keyboard and accessibility behaviour

Frontend Development using React.js or Angular
Chapter 2 of 14