1. Project Overview & Features
The Personal Expense & Budget Tracker is an essential frontend project designed to help you bridge the gap between learning JavaScript syntax and building interactive, responsive web applications.
Key Features Implemented:
- Live summary cards for Total Balance, Income, and Expenses
- Add transaction with description, amount, and income/expense classification
- Transaction history feed with instant deletion
- Automatic persistence with browser localStorage
2. Step-by-Step Code Walkthrough
A Step 1: Semantic HTML Structure
We structure the interface using clean, semantic HTML elements. This ensures maximum accessibility and provides intuitive hooks for CSS classes and JavaScript query selectors.
<div class="expense-app">
<header class="expense-header">
<h2><i class="fas fa-wallet"></i> BudgetWise</h2>
<span class="sub">Expense & Income Manager</span>
</header>
<div class="balance-card">
<span class="lbl">YOUR BALANCE</span>
<h1 id="balance-total">$0.00</h1>
</div>
<div class="inc-exp-container">
<div class="inc-box">
<span>INCOME</span>
<p id="money-plus" class="money plus">+$0.00</p>
</div>
<div class="exp-box">
<span>EXPENSE</span>
<p id="money-minus" class="money minus">-$0.00</p>
</div>
</div>
<form id="transaction-form" class="transaction-form">
<h3>Add New Transaction</h3>
<div class="form-control">
<input type="text" id="text-input" placeholder="e.g., Salary, Coffee, Groceries" required>
</div>
<div class="form-control">
<input type="number" step="0.01" id="amount-input" placeholder="Amount (positive = income, negative = expense)" required>
</div>
<button type="submit" class="btn-add"><i class="fas fa-plus"></i> Add Transaction</button>
</form>
<div class="history-section">
<h3>History</h3>
<ul id="transaction-list" class="transaction-list"></ul>
</div>
</div>B Step 2: Styling & Responsive CSS
Modern CSS flexbox and grid layouts are used to make the UI look polished, centered, and responsive across desktop, tablet, and mobile screens.
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
body {
background: #0f172a;
color: #f8fafc;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.expense-app {
background: #1e293b;
border-radius: 24px;
padding: 28px;
width: 100%;
max-width: 420px;
border: 1px solid #334155;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
}
.expense-header h2 {
font-size: 1.4rem;
color: #10b981;
}
.expense-header .sub {
color: #64748b;
font-size: 0.85rem;
}
.balance-card {
margin: 20px 0;
}
.balance-card .lbl {
font-size: 0.75rem;
color: #94a3b8;
font-weight: 700;
letter-spacing: 1px;
}
.balance-card h1 {
font-size: 2.2rem;
font-weight: 800;
}
.inc-exp-container {
background: #0f172a;
border: 1px solid #334155;
border-radius: 14px;
padding: 16px;
display: flex;
margin-bottom: 24px;
}
.inc-box, .exp-box {
flex: 1;
text-align: center;
}
.inc-box { border-right: 1px solid #334155; }
.inc-exp-container span {
font-size: 0.75rem;
color: #94a3b8;
font-weight: 700;
}
.money {
font-size: 1.25rem;
font-weight: 700;
margin-top: 4px;
}
.money.plus { color: #10b981; }
.money.minus { color: #ef4444; }
.transaction-form h3, .history-section h3 {
font-size: 0.95rem;
color: #cbd5e1;
border-bottom: 1px solid #334155;
padding-bottom: 8px;
margin-bottom: 12px;
}
.form-control {
margin-bottom: 10px;
}
.form-control input {
width: 100%;
padding: 10px 14px;
background: #0f172a;
border: 1px solid #334155;
border-radius: 10px;
color: #f8fafc;
font-size: 0.9rem;
outline: none;
}
.form-control input:focus {
border-color: #10b981;
}
.btn-add {
width: 100%;
background: #10b981;
color: #ffffff;
border: none;
padding: 12px;
border-radius: 10px;
font-weight: 700;
cursor: pointer;
margin-bottom: 20px;
}
.transaction-list {
list-style: none;
max-height: 180px;
overflow-y: auto;
}
.item {
background: #0f172a;
border-radius: 8px;
padding: 10px 12px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
border-left: 5px solid #10b981;
font-size: 0.9rem;
}
.item.minus { border-left-color: #ef4444; }
.delete-btn {
background: none;
border: none;
color: #64748b;
cursor: pointer;
margin-left: 10px;
}
.delete-btn:hover { color: #ef4444; }C Step 3: JavaScript Logic & Event Handling
Here is the complete JavaScript code handling the state, event listeners, mathematical logic, and dynamic DOM rendering:
const STORAGE_KEY = 'budget_transactions_v2';
function loadTransactions() {
try {
const saved = localStorage.getItem(STORAGE_KEY);
if (saved) {
const parsed = JSON.parse(saved);
if (Array.isArray(parsed) && parsed.length > 0) return parsed;
}
} catch (e) {
console.warn('LocalStorage unavailable in sandbox:', e);
}
return [
{ id: 1, text: 'Salary', amount: 2500 },
{ id: 2, text: 'Groceries', amount: -120 },
{ id: 3, text: 'Electricity Bill', amount: -65 }
];
}
function saveTransactions(data) {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
} catch (e) {}
}
let transactions = loadTransactions();
const balance = document.getElementById('balance-total');
const moneyPlus = document.getElementById('money-plus');
const moneyMinus = document.getElementById('money-minus');
const list = document.getElementById('transaction-list');
const form = document.getElementById('transaction-form');
const textInput = document.getElementById('text-input');
const amountInput = document.getElementById('amount-input');
function updateValues() {
const amounts = transactions.map(t => t.amount);
const total = amounts.reduce((acc, item) => (acc += item), 0).toFixed(2);
const income = amounts.filter(item => item > 0).reduce((acc, item) => (acc += item), 0).toFixed(2);
const expense = (amounts.filter(item => item < 0).reduce((acc, item) => (acc += item), 0) * -1).toFixed(2);
balance.innerText = `$${total}`;
moneyPlus.innerText = `+$${income}`;
moneyMinus.innerText = `-$${expense}`;
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function renderList() {
list.innerHTML = '';
transactions.forEach(t => {
const sign = t.amount < 0 ? '-' : '+';
const item = document.createElement('li');
item.classList.add('item', t.amount < 0 ? 'minus' : 'plus');
item.dataset.id = t.id;
item.innerHTML = `
<span>${escapeHtml(t.text)}</span>
<div>
<span>${sign}$${Math.abs(t.amount).toFixed(2)}</span>
<button type="button" class="delete-btn" title="Delete Transaction"><i class="fas fa-trash"></i></button>
</div>
`;
list.appendChild(item);
});
}
list.addEventListener('click', (e) => {
const btn = e.target.closest('.delete-btn');
if (!btn) return;
const item = btn.closest('.item');
if (!item) return;
const id = Number(item.dataset.id);
transactions = transactions.filter(t => t.id !== id);
saveTransactions(transactions);
updateValues();
renderList();
});
form.addEventListener('submit', (e) => {
e.preventDefault();
const text = textInput.value.trim();
const amount = parseFloat(amountInput.value);
if (!text || isNaN(amount)) return;
const newTx = {
id: Date.now(),
text: text,
amount: amount
};
transactions.unshift(newTx);
saveTransactions(transactions);
updateValues();
renderList();
textInput.value = '';
amountInput.value = '';
textInput.focus();
});
updateValues();
renderList();3. Core JavaScript Concepts You Learned
Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.
Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.
Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.
Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.
Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.
4. How to Run Locally on Your Computer
- Download the project ZIP: Click the Download Project (.ZIP) button at the top or in the live runner to save
expense-tracker-project.zip. - Extract the archive: Unzip the downloaded file. Inside you will find separate
index.html,style.css,script.js,index-standalone.html, and a comprehensiveREADME.md. - Open in any browser: Simply double-click
index.html(orindex-standalone.html) to open and test immediately in Chrome, Firefox, Safari, or Edge. - Edit in Visual Studio Code: Open the extracted folder in VS Code. Right-click
index.htmland select "Open with Live Server" to enjoy instant live reloading as you modify HTML, CSS, or JS!
Bonus Challenges for Learners
Ready to level up? Try adding these extra features on your own:
- Add sound effects or audio feedback for user button clicks.
- Add custom theme color customizer with CSS variables.
- Export the results to PDF or copy as a formatted text summary.