Everything you need to go from a blank text editor to a working, useful webpage
Every website you've ever visited — no matter how complex — is built on three foundations: HTML, CSS, and JavaScript. Frameworks and libraries sit on top of these three; they don't replace them. This guide goes deep into each one individually, then walks through building a genuinely useful project — a personal task tracker — combining everything you've learned.
Grab a text editor (VS Code is the standard choice, free, and works on every OS) and a browser. That's the entire toolchain required for everything below.
Part 1: HTML — Structure
HTML (HyperText Markup Language) describes what exists on a page. It isn't a programming language — there's no logic, no calculations — just structure, expressed through tags.
1.1 Anatomy of an HTML Document
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My First Page</title>
</head>
<body>
<h1>Welcome to My Website</h1>
<p>This is my first paragraph.</p>
</body>
</html>
Breaking this down line by line:
-
<!DOCTYPE html>— tells the browser this is a modern HTML5 document. Always the first line, always exactly this. -
<html lang="en">— the root element wrapping everything; thelangattribute helps screen readers and search engines. -
<head>— metadata that isn't visible on the page itself: character encoding, the page title (shown in the browser tab), linked CSS/JS files, and SEO tags. -
<meta name="viewport"...>— without this line, your page won't resize properly on mobile devices. It's easy to forget and causes real problems later — include it in every project from day one. -
<body>— everything the user actually sees goes here.
1.2 Tags, Elements, and Attributes
A tag is the markup itself (<p>). An element is the tag plus its content and closing tag (<p>Hello</p>). An attribute provides extra information inside the opening tag:
<a href="https://example.com" target="_blank">Visit Example</a>
Here, href and target are attributes — href sets the link destination, target="_blank" opens it in a new tab.
Most tags come in pairs (<p>...</p>). A handful are self-closing because they don't wrap content: <img>, <br>, <input>, <meta>.
1.3 The Essential Tags
| Category | Tags | Purpose |
|---|---|---|
| Text |
<h1>–<h6>, <p>, <span>, <strong>, <em>
|
Headings, paragraphs, inline emphasis |
| Structure |
<div>, <header>, <main>, <section>, <footer>, <nav>
|
Grouping and page layout regions |
| Lists |
<ul>, <ol>, <li>
|
Unordered/ordered lists |
| Links & media |
<a>, <img>, <video>
|
Navigation and embedded content |
| Forms |
<form>, <input>, <button>, <label>, <textarea>, <select>
|
Collecting user input |
| Tables |
<table>, <tr>, <td>, <th>
|
Tabular data (not for layout) |
A note on <div> versus the semantic tags (<header>, <main>, <section>, <footer>, <nav>): a <div> carries no meaning — it's a generic box. The semantic tags describe what a section of the page actually is, which helps accessibility tools, search engines, and other developers (including future you) understand the page's structure at a glance. Prefer semantic tags where one fits; fall back to <div> for anything generic.
1.4 Forms — Where Most Real Interactivity Starts
Almost every useful webpage eventually needs to collect input. A basic form looks like this:
<form>
<label for="taskInput">New Task:</label>
<input type="text" id="taskInput" placeholder="Enter a task...">
<button type="submit">Add</button>
</form>
-
<label for="taskInput">links to the<input>with the matchingid— clicking the label focuses the input, and it's important for accessibility. -
type="text"is one of many input types (email,number,checkbox,date,passwordall change behavior and mobile keyboard layout). -
<button type="submit">submits the form by default; you'll often override this behavior with JavaScript, which we'll do in the project below.
1.5 Practicing HTML Alone
Before adding any CSS or JS, build a static page with: a heading, a short bio paragraph, a list of three hobbies, a link to another site, and an image. This forces you to use headings, paragraphs, lists, links, and images without anything else to lean on — the fastest way to get comfortable with structure before layering on style and behavior.
Part 2: CSS — Style
CSS (Cascading Style Sheets) controls appearance: color, spacing, fonts, sizing, and layout.
2.1 Three Ways to Add CSS
<!-- 1. Inline (avoid except for quick testing) -->
<p style="color: blue;">Text</p>
<!-- 2. Internal (fine for small pages) -->
<style>
p { color: blue; }
</style>
<!-- 3. External (the standard for real projects) -->
<link rel="stylesheet" href="style.css">
_
External stylesheets keep structure and style separate, which matters the moment your project grows past a single page._
2.2 Selectors
p { color: #333; } /* every <p> */
.highlight { background: yellow; } /* any element with class="highlight" */
#main-title { font-size: 2rem; } /* the one element with id="main-title" */
.card h2 { margin-bottom: 10px; } /* an <h2> inside an element with class="card" */
button:hover { opacity: 0.8; } /* a button, only while hovered */
Classes vs IDs: use classes (.classname) for anything reused across multiple elements, and IDs (#idname) only for a single, unique element per page — often one you'll also target from JavaScript.
2.3 The Box Model
Every HTML element is a rectangular box made of four layers, from the inside out:
┌─────────────────────────────┐
│ margin │
│ ┌─────────────────────────┐ │
│ │ border │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ padding │ │ │
│ │ │ ┌─────────────────┐ │ │ │
│ │ │ │ content │ │ │ │
│ │ │ └─────────────────┘ │ │ │
│ │ └─────────────────────┘ │ │
│ └─────────────────────────┘ │
└─────────────────────────────┘
.box {
width: 200px;
padding: 20px; /* space inside the border */
border: 2px solid #333;
margin: 10px; /* space outside the border */
}
By default, width only sets the content width — padding and border add on top, which trips up nearly every beginner at some point. Fix it globally with:
* {
box-sizing: border-box;
}
This makes width include padding and border, so a 200px box stays 200px no matter how much padding you add. Add this at the top of every project.
2.4 Flexbox — Modern Layout in a Few Lines
.container {
display: flex;
justify-content: space-between; /* horizontal alignment */
align-items: center; /* vertical alignment */
gap: 16px; /* space between children */
}
display: flex turns a container into a flex container — its direct children automatically line up in a row (by default) and can be aligned, spaced, and reordered without floats or manual positioning. This single property replaces most of the layout hacks that used to dominate CSS.
2.5 Responsive Design With Media Queries
.container {
display: flex;
flex-direction: row;
}
@media (max-width: 600px) {
.container {
flex-direction: column;
}
}
Media queries apply CSS conditionally based on screen size — here, stacking a row layout into a column on small screens. Combined with the viewport <meta> tag from Part 1, this is the foundation of mobile-friendly design.
2.6 Practicing CSS Alone
Take the static page you built in Part 1 and style it: add a background color, style the heading with a custom font size and color, put the hobbies list items in a flex row with spacing between them, and add a hover effect on the link. No JavaScript yet — the goal is getting comfortable translating a visual idea into CSS properties.
Part 3: JavaScript — Behavior
JavaScript is a real programming language — variables, functions, conditionals, and loops all apply, on top of the ability to read and change the page itself.
3.1 Variables
let count = 0; // can be reassigned later
const name = "Mohit"; // cannot be reassigned
Use const by default, and let only when you know the value will change. Avoid var — it behaves inconsistently compared to let and const and has no place in modern code.
3.2 Functions
function greet(name) {
return `Hello, ${name}!`;
}
const greetArrow = (name) => `Hello, ${name}!`; // arrow function, same result
Functions group reusable logic. Arrow functions are a shorter syntax you'll see constantly in modern JavaScript and in every framework built on top of it.
3.3 Conditionals and Loops
const hour = 14;
if (hour < 12) {
console.log("Good morning");
} else if (hour < 18) {
console.log("Good afternoon");
} else {
console.log("Good evening");
}
const tasks = ["Buy milk", "Walk the dog", "Write blog post"];
for (const task of tasks) {
console.log(task);
}
3.4 The DOM — JavaScript's View of Your HTML
The DOM (Document Object Model) is how JavaScript sees your page: not as text, but as a tree of objects it can read and modify live.
document.querySelector('h1'); // first matching element
document.querySelectorAll('.item'); // all matching elements (a list)
document.getElementById('main-title'); // element with that exact id
Once you have an element, you can read or change it:
const heading = document.querySelector('h1');
heading.textContent = "Updated Heading"; // change the text
heading.style.color = "blue"; // change a style directly
heading.classList.add('highlight'); // add a CSS class
3.5 Events — Responding to the User
const button = document.querySelector('button');
button.addEventListener('click', function () {
alert('Button was clicked!');
});
addEventListener is the core pattern of interactive JavaScript: pick an element, pick an event (click, submit, keydown, input, change), and provide a function to run when it happens.
3.6 Arrays and Common Array Methods
Most real UI work involves lists of things — tasks, products, comments — stored as arrays:
const tasks = ["Buy milk", "Walk the dog"];
tasks.push("Write blog post"); // add to the end
tasks.splice(1, 1); // remove 1 item at index 1
tasks.forEach(task => console.log(task)); // run code for each item
const upper = tasks.map(task => task.toUpperCase()); // transform each item into a new array
forEach and map will come up constantly once you start rendering lists of data onto a page — they're worth being genuinely comfortable with before moving on.
3.7 Practicing JavaScript Alone
Using the page from Parts 1 and 2, add a button that, when clicked, changes the heading's text and background color. Then add a second button that adds a new item to your hobbies list. This is deliberately close to what real UI code does constantly: respond to a click, then update the DOM.
Part 4: The Project — A Task Tracker
Time to combine everything into something genuinely useful: a task tracker that lets you add tasks, mark them complete, delete them, and see a live count — all saved so it doesn't disappear on refresh.
4.1 The HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Task Tracker</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="app">
<h1>Task Tracker</h1>
<form id="taskForm">
<input type="text" id="taskInput" placeholder="Add a new task..." required>
<button type="submit">Add</button>
</form>
<p id="taskCount">0 tasks remaining</p>
<ul id="taskList"></ul>
</div>
<script src="script.js"></script>
</body>
</html>
4.2 The CSS
* {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Arial, sans-serif;
background-color: #f4f6f8;
display: flex;
justify-content: center;
padding-top: 40px;
margin: 0;
}
.app {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 400px;
}
h1 {
text-align: center;
color: #2c3e50;
margin-bottom: 20px;
}
#taskForm {
display: flex;
gap: 8px;
margin-bottom: 12px;
}
#taskInput {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 14px;
}
button {
background-color: #3498db;
color: white;
border: none;
padding: 10px 16px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background-color: #2980b9;
}
#taskCount {
color: #7f8c8d;
font-size: 14px;
margin-bottom: 10px;
}
#taskList {
list-style: none;
padding: 0;
margin: 0;
}
.task-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px;
border-bottom: 1px solid #eee;
}
.task-item span {
flex: 1;
cursor: pointer;
}
.task-item.completed span {
text-decoration: line-through;
color: #aaa;
}
.delete-btn {
background-color: #e74c3c;
padding: 4px 10px;
font-size: 12px;
}
.delete-btn:hover {
background-color: #c0392b;
}
4.3 The JavaScript
const taskForm = document.getElementById('taskForm');
const taskInput = document.getElementById('taskInput');
const taskList = document.getElementById('taskList');
const taskCount = document.getElementById('taskCount');
// Load saved tasks, or start with an empty list
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
function saveTasks() {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
function updateCount() {
const remaining = tasks.filter(task => !task.completed).length;
taskCount.textContent = `${remaining} task${remaining !== 1 ? 's' : ''} remaining`;
}
function renderTasks() {
taskList.innerHTML = '';
tasks.forEach((task, index) => {
const li = document.createElement('li');
li.className = 'task-item' + (task.completed ? ' completed' : '');
const span = document.createElement('span');
span.textContent = task.text;
span.addEventListener('click', () => toggleComplete(index));
const deleteBtn = document.createElement('button');
deleteBtn.textContent = 'Delete';
deleteBtn.className = 'delete-btn';
deleteBtn.addEventListener('click', () => deleteTask(index));
li.appendChild(span);
li.appendChild(deleteBtn);
taskList.appendChild(li);
});
updateCount();
}
function addTask(text) {
tasks.push({ text, completed: false });
saveTasks();
renderTasks();
}
function toggleComplete(index) {
tasks[index].completed = !tasks[index].completed;
saveTasks();
renderTasks();
}
function deleteTask(index) {
tasks.splice(index, 1);
saveTasks();
renderTasks();
}
taskForm.addEventListener('submit', function (event) {
event.preventDefault(); // stop the page from reloading on submit
const text = taskInput.value.trim();
if (text === '') return;
addTask(text);
taskInput.value = '';
});
renderTasks(); // render whatever was loaded from storage on page load
4.4 How This All Fits Together
Walking through what happens when you use this app ties every earlier section together:
-
On page load, JavaScript checks
localStoragefor previously saved tasks and renders them — this is why refreshing the page doesn't wipe your list. -
Typing and submitting the form triggers the
submitevent listener.event.preventDefault()stops the browser's default behavior (reloading the page), which is essential — without it, every submission would refresh and lose your JavaScript state. -
addTaskpushes a new object into thetasksarray, saves the whole array tolocalStorageas a JSON string, and callsrenderTasks()to redraw the list. -
renderTasksclears the existing list and rebuilds it from scratch usingforEach, creating real DOM elements (createElement) for each task rather than writing raw HTML strings — a safer, more standard approach. -
Clicking a task's text calls
toggleComplete, flipping itscompletedflag, which the CSS then reflects instantly (.completed spangets a strikethrough) becauserenderTasksre-applies thecompletedclass. -
Clicking Delete calls
deleteTask, which usesspliceto remove that one item from the array before re-rendering.
Every concept from Parts 1–3 shows up here: semantic HTML and a form (Part 1), Flexbox and the box model for layout (Part 2), and DOM manipulation, events, arrays, and functions tying it all together (Part 3).
What to Build Next
Once this project makes sense end-to-end, natural next steps that build on the same patterns:
- Add an "edit task" feature (click a task to turn it into an editable input)
- Add categories or priority levels with different colors
- Add a "clear completed" button
- Fetch a real to-do list from a public API instead of starting empty
Each of these reuses the exact same core loop — read the DOM, update an array, re-render, save — which is, not coincidentally, the same core loop every JavaScript framework (React included) is built to manage for you automatically once your projects grow large enough to need it.
Final Thoughts
HTML, CSS, and JavaScript aren't a "beginner phase" to rush past on the way to frameworks — they're the actual foundation everything else sits on. The task tracker above is intentionally simple, but it's a genuinely complete, working, useful piece of software: it takes input, stores data, updates the interface, and persists state across page reloads. That's the same fundamental loop behind almost every web application you'll ever build, no matter how much more complex the tools around it eventually get.
Save the three files, open index.html in your browser, and you have a working app — built entirely from the ground up.
Top comments (0)