Skip to main content
Published / updated

Web Development Foundation

Before you start

You need: stage 5, so there is an API to call.

Time: 36–44 classes.

Learning objective

Build the browser skills both frontend paths assume — semantic HTML, responsive CSS, JavaScript, the DOM, and calling the API from stage 5.

Topics

  • Semantic HTML and forms
  • CSS, the box model, Flexbox and Grid
  • Responsive design
  • JavaScript fundamentals, arrays and objects
  • The DOM and events
  • JSON, fetch, promises and async/await
  • Essential jQuery
  • TypeScript basics

What this stage covers

Angular and React are both built on this. A component is HTML, CSS and TypeScript with a framework arranging them. Skipping to a framework without this stage means every layout problem and every undefined becomes framework magic rather than something you can reason about.

ConceptUsed later in
Semantic HTMLComponent templates, stage 7
CSS layout and responsivenessEvery screen
JavaScript arrays and objectsRendering lists
The DOM and eventsWhat frameworks do for you
fetch and async/awaitCalling the stage 5 API
JSONThe payload format throughout
TypeScriptAngular requires it; React benefits

Three rules established here recur in every remaining stage:

  • Client-side validation is a convenience, never a control. The API from stage 5 validates again, because Postman skips the browser entirely.
  • CORS is fixed on the server. Nothing you write in JavaScript changes it, and a request that works in Postman and fails in the browser is the signature.
  • The token lives in browser storage and is sent on every request. HTTP is stateless; the server remembers nothing.

Worked flow: the fee receipt

The receipt for Ravi Kumar (NCA-2024-0012) as a plain page calling the stage 5 API — before any framework.

<main class="receipt">
<h1>Fee Receipt</h1>

<dl class="receipt-details">
<dt>Receipt number</dt> <dd id="receiptNumber"></dd>
<dt>Student</dt> <dd id="studentName"></dd>
<dt>Roll number</dt> <dd id="rollNumber"></dd>
<dt>Class</dt> <dd id="className"></dd>
<dt>Amount paid</dt> <dd id="amountPaid"></dd>
<dt>Paid on</dt> <dd id="paidOn"></dd>
<dt>Balance</dt> <dd id="balance"></dd>
</dl>

<button id="printButton" type="button">Print</button>
</main>

<dl>, <dt> and <dd> are semantic — a description list is exactly what a receipt is. A screen reader announces the pairs correctly, and the print stylesheet can target them.

async function loadReceipt(paymentId) {
const token = localStorage.getItem("token");

const response = await fetch(`/api/fee-payments/${paymentId}/receipt`, {
headers: { "Authorization": `Bearer ${token}` }
});

if (response.status === 401) {
window.location.href = "/login";
return;
}

if (response.status === 404) {
document.getElementById("receiptNumber").textContent = "Receipt not found";
return;
}

if (!response.ok) {
document.getElementById("receiptNumber").textContent = "Could not load the receipt";
return;
}

const receipt = await response.json();

document.getElementById("receiptNumber").textContent = receipt.receiptNumber;
document.getElementById("studentName").textContent = receipt.studentName;
document.getElementById("rollNumber").textContent = receipt.rollNumber;
document.getElementById("className").textContent = receipt.className;
document.getElementById("amountPaid").textContent = formatCurrency(receipt.amountPaid);
document.getElementById("paidOn").textContent = formatDate(receipt.paidOn);
document.getElementById("balance").textContent = formatCurrency(receipt.balanceAfterPayment);
}

Every status code is handled separately, because each needs a different response — 401 redirects, 404 shows a message, 500 shows a different one. if (!response.ok) alone treats all three the same.

The token is read from storage and sent on every request. No cookie, no session on the server.

Note the camelCase field names. The API returns receiptNumber, not ReceiptNumber — the JSON convention differs from C#, and expecting the wrong casing produces undefined with no error.

@media print {
.no-print, #printButton, nav {
display: none;
}

.receipt {
max-width: 100%;
}

.receipt::after {
content: "nexcoding.in";
display: block;
margin-top: 2rem;
font-size: 0.75rem;
color: #666;
}
}

A print stylesheet is a real requirement here — the clerk prints this. Hide the navigation and the button; keep the content.

Where to learn it

TopicRead
HTML structureTrack 09 — HTML structure
Tables and formsTrack 09 — Tables and forms
Semantic layoutTrack 09 — Semantic layout
CSS fundamentalsTrack 09 — CSS fundamentals
Flexbox and GridTrack 09 — CSS layout
Responsive designTrack 09 — Responsive design
JavaScript fundamentalsTrack 09 — JavaScript fundamentals
Arrays and objectsTrack 09 — Arrays and objects
The DOM and eventsTrack 09 — DOM and events
JSON, fetch, asyncTrack 09 — JSON, fetch, async
jQuery and TypeScriptTrack 09 — jQuery and TypeScript
The frontend capstoneTrack 09 — Frontend project

Tools: Track 15 — Browser and frontend tooling.

Stage exercises

From the guided path syllabus:

Build a semantic form. A fee payment form with labels bound to inputs, correct input types, and validation attributes. Then submit it from Postman without any of them.

Recreate a responsive card layout. A student list that is a table on desktop and cards on mobile.

Render API data. Call the stage 5 receipt endpoint and render it, handling 200, 401, 404 and 500 differently.

Write a typed API client. In TypeScript, define a FeeReceipt interface matching the DTO and a getReceipt(paymentId: number): Promise<FeeReceipt> function.

Debugging drills

Fix layout overflow. Make a table overflow on mobile, find it in the Elements panel, and fix it with an overflow-x: auto container.

Investigate a console error. Render a receipt before the fetch resolves and read Cannot read properties of undefined.

Trace a failed network request. Call the API with an expired token, read the 401 in the Network tab, and decode the token to confirm exp is in the past.

Meet CORS. Serve the page from localhost:5173 against the API on localhost:7099 with no CORS policy. Read the error, confirm the same request works in Postman, then fix it on the server.

Practice

  1. Work through Track 09's twelve articles.
  2. Build the receipt page above and connect it to your stage 5 API.
  3. Handle 200, 401, 404 and 500 separately, and trigger each.
  4. Use ReceiptNumber instead of receiptNumber and watch the field render as undefined.
  5. Write a print stylesheet that hides navigation and adds the nexcoding.in footer.
  6. Build a fee payment form with required and min, then submit an invalid payment from Postman.
  7. Make the student list responsive — table on desktop, cards on mobile.
  8. Break CSS three ways — specificity, a wrong selector, a file that fails to load — and identify each in the Styles pane.
  9. Trigger a CORS error and fix it on the server, not the client.
  10. Store a token in localStorage, decode it at jwt.io, and confirm the exp claim.
  11. Define TypeScript interfaces for FeeReceipt and RecordPaymentRequest, and note the compile error when a field name is wrong.

Exercises 6 and 9 are the two that decide whether you understand where control actually lives.

You can now

  • Build responsive, semantic pages
  • Write JavaScript that manipulates the DOM and handles events
  • Call your API with fetch and handle each status code separately
  • Say why client-side validation is never the control
  • Write foundational TypeScript

Review questions

  1. Why does client-side validation not remove the need for server-side validation?
  2. Why does a request that works in Postman fail in the browser with a CORS error?
  3. What happens when you read ReceiptNumber from a JSON response that contains receiptNumber?
  4. Why must the token be sent on every request rather than once at sign-in?

Next: Choose Angular or React