Your first project is small on purpose: a number you can increase, decrease and reset. It uses variables, functions, events and the DOM, the core of everything else.
What you will build
A counter with three buttons. It never goes below zero, and the page tells screen readers when the number changes.
<style>
.counter { font-family: system-ui, sans-serif; text-align: center; max-width: 260px; margin: 0 auto; }
.value { font-size: 56px; font-weight: 700; margin: 8px 0; }
button { font: inherit; padding: 8px 16px; margin: 0 4px; border-radius: 6px; border: 2px solid #1a5276; background: #fff; cursor: pointer; }
button:hover { background: #1a5276; color: #fff; }
button:focus-visible { outline: 3px solid #d94a00; outline-offset: 2px; }
button:disabled { opacity: .4; cursor: not-allowed; }
</style>
<div class="counter">
<div class="value" id="value" role="status" aria-live="polite">0</div>
<button id="down" aria-label="Decrease">−</button>
<button id="reset">Reset</button>
<button id="up" aria-label="Increase">+</button>
</div>
<script>
let count = 0;
const valueEl = document.querySelector("#value");
const downBtn = document.querySelector("#down");
function render() {
valueEl.textContent = count;
downBtn.disabled = count === 0; // cannot go below zero
}
document.querySelector("#up").addEventListener("click", () => { count++; render(); });
downBtn.addEventListener("click", () => { if (count > 0) { count--; render(); } });
document.querySelector("#reset").addEventListener("click", () => { count = 0; render(); });
render();
</script>
How it works
- State: one variable,
count, is the single source of truth. render()updates the page from the state. Every button changescountand then callsrender(). Keeping that rule makes the page and the data impossible to get out of sync.- The minus button is disabled at zero, a clearer experience than an error.
aria-live="polite"announces changes to screen readers, and the buttons have text labels (aria-label) because “−” and “+” are not meaningful alone.
Try changing it
- Add a step input so the buttons add 5 or 10.
- Add a maximum of 10 and disable the plus button there.
- Save the count with
localStorageso it survives a reload (localStorage). - Make the number turn red below 3 using a class (classList).
Exercise: keyboard shortcuts
Make the counter respond to the keys ArrowUp (increase), ArrowDown (decrease) and 0 (reset), without breaking the buttons.
Show a possible solution
document.addEventListener("keydown", event => {
if (event.key === "ArrowUp") { count++; render(); }
else if (event.key === "ArrowDown" && count > 0) { count--; render(); }
else if (event.key === "0") { count = 0; render(); }
});
Add this inside the script after render() is defined. See Keyboard Events.