HTML, CSS and JavaScript for Beginners: Building an Interactive Web Page
Modern websites combine three technologies that work together. HTML defines the structure of the content, CSS defines its appearance and layout, and JavaScript makes the page respond to what the user does, such as clicking a button, typing text or choosing a tab. This article builds a small example that brings all three together, explains what each part does, and shows why it helps to keep their responsibilities separate.
Understanding each language's role
Think of building a house. HTML is the structure and the walls: it says where the living room, kitchen and doors are. CSS is the paint, materials and furniture arrangement that make the house pleasant to live in. JavaScript is the electrical system and switches: press a switch and the light comes on, open a door and a bell rings.
Keeping these roles apart is very useful. To change the colours across a whole site, you edit only the CSS and leave the structure alone. To add interaction, you write JavaScript without altering the appearance. This makes code easier to read and maintain over time.
Project structure
Create a folder called `interactive-demo` containing three files.
- `index.html` for the structure
- `style.css` for the styling
- `script.js` for the interactive behaviour
Keep the three files in the same folder so that the references in the HTML work without full addresses.
Step 1: the HTML structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Score Counter</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="card">
<h1>Score Counter</h1>
<p id="score-text">Current score: <span id="score">0</span></p>
<div class="buttons">
<button id="add-btn" type="button">+1 point</button>
<button id="reset-btn" type="button">Reset</button>
</div>
<p id="message" aria-live="polite"></p>
</main>
<script src="script.js"></script>
</body>
</html>
This code has three key groups of elements: the score number with `id="score"`, two buttons each with their own `id`, and the message paragraph `id="message"` used to show notices. The attribute `aria-live="polite"` tells screen readers to announce new text when it changes, so users who cannot see the screen receive the same information as everyone else.
Placing `<script>` at the end of `<body>` means JavaScript starts only after all the elements on the page have loaded, so it can find them by `id` without errors.
Step 2: styling with CSS
:root {
--bg: #f0f4fb;
--card: #ffffff;
--accent: #2255aa;
--text: #1a1a1a;
}
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg);
color: var(--text);
font-family: system-ui, sans-serif;
}
.card {
background: var(--card);
padding: 32px;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
text-align: center;
width: min(90vw, 360px);
}
.buttons {
display: flex;
gap: 12px;
justify-content: center;
}
button {
padding: 10px 18px;
border: none;
border-radius: 8px;
background: var(--accent);
color: #fff;
font-size: 16px;
cursor: pointer;
transition: transform 0.1s ease, opacity 0.2s ease;
}
button:hover {
opacity: 0.9;
}
button:active {
transform: scale(0.96);
}
#reset-btn {
background: #8a94a8;
}
Reading the rules one at a time:
- `:root` stores colour variables. To change the main colour of the site, you edit one line. Variables are used with the form `var(--name)`.
- `body` uses Flexbox to centre the card both vertically and horizontally. `min-height: 100vh` makes the layout fill the whole screen.
- `.card` starts with a dot because it is a class, which can be shared by several elements. `width: min(90vw, 360px)` stops the card growing too wide on large screens while still fitting on phones.
- `button:hover` and `button:active` describe the states when the mouse is over the button and when it is pressed, so the user feels that the button responds.
- `#reset-btn` starts with `#` because it targets an id, which identifies exactly one element.
The difference between a class and an id matters. A class groups elements that share a style, while an id identifies one specific element.
Step 3: making the page interactive with JavaScript
const scoreEl = document.getElementById("score");
const addBtn = document.getElementById("add-btn");
const resetBtn = document.getElementById("reset-btn");
const messageEl = document.getElementById("message");
let score = 0;
function render() {
scoreEl.textContent = score;
if (score >= 10) {
messageEl.textContent = "Excellent! You reached 10 points.";
} else {
messageEl.textContent = "";
}
}
addBtn.addEventListener("click", function () {
score = score + 1;
render();
});
resetBtn.addEventListener("click", function () {
score = 0;
render();
});
render();
The file divides its work into clear parts.
1. `document.getElementById` finds elements in the HTML by their id and stores them in variables. Looking them up once at the start makes the code faster and easier to read.
2. `let score = 0` holds the state of the program. This value is the single source of truth that the whole program uses.
3. The function `render` reads `score` and updates the screen to match. This is called rendering from state. Instead of changing the text on screen in every action, you change the value and let the drawing function refresh the display.
4. `addEventListener("click", ...)` attaches a function to the click event. Whenever the user clicks the button, that function runs.
5. The final call to `render()` displays the starting score of 0 as soon as the page loads.
Try it: each press of +1 increases the number, reaching 10 shows the message, and Reset returns the count to zero.
Step 4: making the code more robust
The example works, but several habits will make your code stronger.
- **Check that an element exists:** If an id is mistyped, `getElementById` returns `null`, and the code will fail as soon as it uses that value. In bigger projects, check before using it.
- **Use `const` by default:** Use `let` only when the value really changes, such as `score`.
- **Keep functions focused:** Each function should do one job. `render` only displays the state and does not calculate the score itself.
An example of a safety check:
if (!scoreEl) {
console.error("Element #score was not found in the HTML");
}
`console.error` prints the message in the browser's Console tab (press F12), which helps you locate problems quickly.
CSS or JavaScript? A simple rule
Beginners sometimes wonder which language should handle a given effect. A simple rule helps. If the change is purely visual and does not depend on what the user has done, use CSS. Examples include a colour change on hover or the spacing between elements. If the behaviour depends on data or actions, such as counting clicks or storing typed values, use JavaScript.
For example, changing a button's colour on hover can be done with CSS `:hover`, but counting how many times the button was pressed needs JavaScript.
Switching appearance with a class
Suppose you want the card to change colour when the score reaches 10. Instead of writing style rules in JavaScript, add a class from JavaScript and define the look in CSS.
.card.success {
background: #e6f7ec;
border: 2px solid #2e9e5b;
}
const cardEl = document.querySelector(".card");
function render() {
scoreEl.textContent = score;
if (score >= 10) {
messageEl.textContent = "Excellent! You reached 10 points.";
cardEl.classList.add("success");
} else {
messageEl.textContent = "";
cardEl.classList.remove("success");
}
}
This keeps responsibilities clear. JavaScript says "the state is now success", and CSS decides what success looks like. You can redesign the look without touching the JavaScript.
Extra example: a simple tab menu
This example changes content when a button is clicked, a pattern that appears on many real websites.
<nav>
<button class="tab-btn active" data-tab="home">Home</button>
<button class="tab-btn" data-tab="about">About</button>
</nav>
<section class="tab-panel active" id="home">Welcome to my website.</section>
<section class="tab-panel" id="about">I am a student learning to build websites.</section>
.tab-panel { display: none; }
.tab-panel.active { display: block; }
.tab-btn.active { background: #2255aa; color: #fff; }
const buttons = document.querySelectorAll(".tab-btn");
const panels = document.querySelectorAll(".tab-panel");
buttons.forEach(function (btn) {
btn.addEventListener("click", function () {
const target = btn.dataset.tab;
buttons.forEach(b => b.classList.remove("active"));
panels.forEach(p => p.classList.remove("active"));
btn.classList.add("active");
document.getElementById(target).classList.add("active");
});
});
`querySelectorAll` returns a list of every element matching the selector, and `forEach` loops through it to attach the click handler to each button. `btn.dataset.tab` reads the value of the `data-tab` attribute, so one function serves every button instead of writing a separate function for each.
Common JavaScript mistakes
- **Using elements before the HTML has loaded:** Fix it by placing `<script>` at the end of `<body>` or by adding `defer` to the script tag.
- **Missing brackets or quotation marks:** Check that every bracket closes, and read the browser Console when an error appears.
- **Comparing with `=` instead of `===`:** Use `===` for a strict comparison of values.
- **Updating the page repeatedly inside a loop:** Gather changes and update once where possible, so the page stays smooth.
Guidance for beginners
- Write the HTML first, and check that the page reads sensibly before adding CSS and JavaScript.
- Add CSS in small steps and check the result in the browser after each change.
- Add JavaScript one function at a time and test before moving on.
- Use the Console to find problems, and read error messages completely before changing code.
- Practise by building your own small projects, such as a counter, a clock, a calculator or a to-do list.
How to save and run the project
1. Create a new folder called `interactive-demo`.
2. Create the three files in that folder: `index.html`, `style.css` and `script.js`, and paste in the code from each step.
3. Save the files as UTF-8. In Notepad, choose "Save As" and set the encoding to UTF-8, otherwise non-English characters may appear garbled.
4. Check the file names. On Windows, turn on file name extensions first, so that you do not end up with a file called `script.js.txt`.
5. Double-click `index.html`, or drag it into the browser window.
6. If you use VS Code, install the Live Server extension, right-click `index.html` and choose "Open with Live Server". The page then refreshes automatically each time you save.
Results from an actual run
These results come from running the score counter in a real browser and pressing the buttons in order. The test used the Thai version of the code, so the messages below are in Thai.
- **Start:** score 0, no message, white card.
- **Press +1 nine times:** score 9, no message, card still white.
- **Press +1 a tenth time:** score 10, the message "ยอดเยี่ยม! คุณทำได้ถึง 10 คะแนนแล้ว" appears, and the card turns green because JavaScript adds the `success` class.
- **Press Reset:** score returns to 0, the message disappears and the card returns to white because JavaScript removes the `success` class.
The tab menu example was also run in the browser. Starting on the home tab, pressing "About" showed the About panel and highlighted that button, and pressing "Home" brought back the home panel. The messages in that test were also in Thai; the behaviour is the same in any language.
Next steps
Once you are comfortable with these three languages, you can explore CSS Flexbox and Grid for advanced layouts, `fetch` and JSON for loading data from a server, Git for managing your code, and frameworks such as React when projects grow larger.
Conclusion
HTML provides the structure, CSS provides the appearance, and JavaScript provides the interaction. Keeping these three responsibilities separate makes code easier to understand, change and extend. Start with small examples such as a score counter or a tab menu, then add complexity step by step. Writing and testing code yourself every day will build your skills much faster than reading alone.