Expense Tracker with HTML, CSS & JavaScript
20 DAYS 20 PROJECT CHALLENGE
Day #07
Project Overview
A simple, clean Expense Tracker that lets users add income and expenses, store them permanently in LocalStorage, and automatically calculate totals using map() and reduce(). The tracker shows a running balance, separate income & expense totals, and lets you delete transactions.
This project teaches:
DOM manipulation
Array methods (
map,filter,reduce)LocalStorage (saving + loading persistent data)
Building reusable UI render functions
Handling form inputs and validation
Key Features
- Add income (positive) or expense (negative).
- Fully persistent using LocalStorage.
- Summary section:
- Total Balance
- Total Income
- Total Expense
- Transaction history with:
- Colored indicators (green for income, red for expense)
- Delete button
- Clean UI and responsive layout
- Automatically recalculates totals after each change
HTML Code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Day 7 — Expense Tracker</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main class="card">
<header>
<div class="logo">ET</div>
<div>
<h1>Day 7: Expense Tracker</h1>
<p class="lead">Track income & expenses, view your balance, and store data using LocalStorage.</p>
</div>
</header>
<!-- BALANCE SUMMARY -->
<section class="summary">
<div class="box">
<h3>Balance</h3>
<p id="balance" class="big">₹0</p>
</div>
<div class="box green">
<h3>Income</h3>
<p id="income">₹0</p>
</div>
<div class="box red">
<h3>Expense</h3>
<p id="expense">₹0</p>
</div>
</section>
<!-- ADD TRANSACTION -->
<section class="add">
<h2>Add Transaction</h2>
<label>
<span>Description</span>
<input id="desc" type="text" placeholder="e.g. Salary, Groceries" />
</label>
<label>
<span>Amount (use - for expense)</span>
<input id="amount" type="number" placeholder="e.g. 500 or -200" />
</label>
<button id="addBtn" class="btn">Add Transaction</button>
</section>
<!-- HISTORY -->
<section>
<h2>History</h2>
<ul id="list" class="list"></ul>
</section>
</main>
<script src="script.js"></script>
</body>
</html>
CSS Code
:root {
--bg: #071026;
--card: #0b1220;
--muted: #9aa4b2;
--green: #10b981;
--red: #ef4444;
--white: #e6eef6;
font-family: Inter, system-ui, sans-serif;
}
body {
margin: 0;
background: #002252;
color: var(--white);
display: flex;
justify-content: center;
padding: 30px;
}
.card {
width: min(650px, 100%);
background: rgba(255,255,255,0.03);
padding: 20px;
border-radius: 14px;
border: 1px solid rgba(255,255,255,0.05);
box-shadow: 0 8px 30px rgba(0,0,0,0.5);
}
header {
display: flex;
align-items: center;
gap: 15px;
}
.logo {
width: 44px;
height: 44px;
border-radius: 10px;
background: linear-gradient(135deg, #7c3aed, #22c1c3);
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
}
.lead {
color: var(--muted);
margin-top: 4px;
font-size: 14px;
}
.summary {
display: flex;
justify-content: space-between;
margin-top: 20px;
gap: 15px;
}
.box {
flex: 1;
padding: 15px;
border-radius: 10px;
background: rgba(255,255,255,0.05);
text-align: center;
}
.box.green {
border-left: 4px solid var(--green);
}
.box.red {
border-left: 4px solid var(--red);
}
.big {
font-size: 26px;
font-weight: 700;
}
.add {
margin-top: 25px;
}
label {
display: block;
margin: 12px 0;
}
label span {
display: block;
margin-bottom: 5px;
color: var(--muted);
}
input {
width: 100%;
padding: 10px;
background: transparent;
border: 1px solid rgba(255,255,255,0.1);
border-radius: 8px;
color: var(--white);
outline: none;
}
.btn {
width: 100%;
margin-top: 10px;
padding: 12px;
background: linear-gradient(90deg, #7c3aed, #22c1c3);
border: 0;
border-radius: 10px;
font-weight: 600;
color: white;
cursor: pointer;
}
.list {
margin-top: 15px;
padding: 0;
list-style: none;
}
.list li {
display: flex;
justify-content: space-between;
background: rgba(255,255,255,0.04);
border-left: 4px solid;
padding: 12px;
margin: 8px 0;
border-radius: 8px;
align-items: center;
}
.list .text {
flex: 1;
margin-left: 8px;
}
.list .amount.green {
color: var(--green);
font-weight: 700;
}
.list .amount.red {
color: var(--red);
font-weight: 700;
}
.del-btn {
background: transparent;
border: 0;
color: var(--muted);
cursor: pointer;
font-size: 18px;
}
Javascript Code
// Select elements
const desc = document.getElementById("desc");
const amount = document.getElementById("amount");
const addBtn = document.getElementById("addBtn");
const list = document.getElementById("list");
const balanceEl = document.getElementById("balance");
const incomeEl = document.getElementById("income");
const expenseEl = document.getElementById("expense");
// Load from LocalStorage or start empty
let transactions = JSON.parse(localStorage.getItem("transactions")) || [];
// Save to LocalStorage
function saveData() {
localStorage.setItem("transactions", JSON.stringify(transactions));
}
// Format numbers as rupees
function formatMoney(n) {
return "₹" + n.toLocaleString("en-IN");
}
// Render all items
function renderList() {
list.innerHTML = "";
transactions.forEach((t) => {
const li = document.createElement("li");
li.style.borderColor = t.amount > 0 ? "#10b981" : "#ef4444";
li.innerHTML = `
<span class="text">${t.desc}</span>
<span class="amount ${t.amount > 0 ? "green" : "red"}">${formatMoney(t.amount)}</span>
<button class="del-btn" onclick="deleteItem(${t.id})">✖</button>
`;
list.appendChild(li);
});
updateSummary();
}
// Update totals using reduce()
function updateSummary() {
const amounts = transactions.map((t) => t.amount);
const total = amounts.reduce((acc, v) => acc + v, 0);
const income = amounts.filter((v) => v > 0).reduce((a, b) => a + b, 0);
const expense = amounts.filter((v) => v < 0).reduce((a, b) => a + b, 0);
balanceEl.textContent = formatMoney(total);
incomeEl.textContent = formatMoney(income);
expenseEl.textContent = formatMoney(expense);
}
// Add transaction
addBtn.addEventListener("click", () => {
const d = desc.value.trim();
const a = Number(amount.value);
if (!d || !amount.value) {
alert("Please enter both description and amount.");
return;
}
const t = {
id: Date.now(),
desc: d,
amount: a
};
transactions.push(t);
saveData();
renderList();
desc.value = "";
amount.value = "";
});
// Delete item
function deleteItem(id) {
transactions = transactions.filter((t) => t.id !== id);
saveData();
renderList();
}
// INITIAL RENDER
renderList();
Related Projects
Day 5 : Digital Piano
Play sound notes when user clicks keys or presses keyboard buttons.
Concepts: Audio API, keyboard events.
Day 9 : Flashcard Learning App
Create and flip flashcards to study and test your knowledge.
Concepts: DOM manipulation, CSS 3D effects, LocalStorage.
Day 10 : Typing Speed Test
Measures typing speed and accuracy.
Concepts: Timers, string comparison, event listeners.