Expense Tracker with HTML, CSS & JavaScript

20 DAYS 20 PROJECT CHALLENGE

Day #07

Project Overview

The Expense Tracker is a beginner-friendly project built using HTML, CSS, and JavaScript. It allows users to add and manage their income and expenses at one place. Users can enter a description and amount for each transaction, where positive amounts are treated as income and negative amounts are treated as expenses.

The application calculates the current balance, the sum of all incomes, and the sum of all expenses every time the user adds or deletes an item. All the data is stored in the LocalStorage of the browser. Therefore, it will not be lost when the page is reloaded. This project is useful for practicing important JavaScript concepts such as DOM manipulation, arrays, event handling, LocalStorage, and array methods like map(), filter(), and reduce().

Key Features

  • Add Income and Expenses: Users can add new income by entering the description and amount of money. In this application, negative numbers are expenses, and positive numbers are income.
  • Current Balance: The application automatically calculates the current balance based on the added income and expenses.
  • Income Summary: All positive transactions are calculated together to display the total amount of income that has been added to the Expense Tracker.
  • Expense Summary: All negative transactions are calculated together to show the total amount spent through the application.
  • Transaction History: The application stores all the transactions in the history section with their descriptions and amounts.
  • Visual Difference Between Income and Expenses: The application highlights the difference between income and expenses using various visuals, such as colors.
  • Delete Transactions: Each transaction includes a delete button. Users can remove an unwanted transaction, and the balance and summary values are automatically updated.
  • Works Offline: The application stores all the transactions history in the local storage of the user’s browser. As a result, the data remains even after refreshing the page.
  • Auto Calculation: JavaScript recalculates the balance, income, and expense totals whenever users add or delete a transaction.
  • Responsive and Clean Design: The application has a clean design that makes the transaction form, summary section, and history list easy to read on different screen sizes.

What You'll Learn

  • Understand how to collect user input using HTML form elements.
  • Learn how to create and store objects inside a JavaScript array.
  • Practice using map() to extract values from an array.
  • Understand how filter() can separate income and expense transactions.
  • Learn how reduce() can calculate totals from multiple values.
  • Practice creating HTML elements dynamically using JavaScript.
  • Understand how to update the DOM when data changes.
  • Learn how LocalStorage can save data in the browser.
  • Practice converting JavaScript data using JSON.stringify() and JSON.parse().
  • Learn how to delete items from an array using filter().

HTML Code

The HTML code creates the structure of the Expense Tracker. The main content is placed inside the .card container, which holds the project heading, balance summary, transaction form, and transaction history. The summary section contains separate elements with the IDs balance, income, and expense. JavaScript updates these elements to display the current financial information. The transaction form includes the #desc input for entering a transaction description and the #amount input for entering the amount.

The #addBtn button adds the transaction to the application. Below the form, the #list element provides a place where JavaScript dynamically displays all transactions. Finally, the script.js file is linked at the bottom of the <body>. This allows JavaScript to access the HTML elements and add the required functionality.

<!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

The CSS code controls the appearance and layout of the Expense Tracker. The :root section defines reusable values for colors such as the background, card, muted text, green income color, and red expense color. The body uses Flexbox to center the main .card container on the page. The .summary class uses Flexbox to arrange the Balance, Income, and Expense boxes next to each other.

The .box.green and .box.red selectors provide different visual indicators for income and expense information. The input fields and .btn button are styled to create a clean transaction form. The .list li selector styles each transaction in the history section. Income and expense amounts receive different colors through the .green and .red classes. The .del-btn class styles the delete button used to remove transactions.

: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

The JavaScript code handles the main functionality of the Expense Tracker. First, it selects the input fields, Add Transaction button, transaction list, and summary elements from the HTML. The transactions array stores all income and expense transactions. When the application loads, JSON.parse() retrieves previously saved transactions from LocalStorage. The saveData() function then saves any changes back to LocalStorage using JSON.stringify().

The renderList() function creates and displays each transaction in the history list. It also uses different colors depending on whether the amount is positive or negative. The updateSummary() function uses map(), filter(), and reduce() to calculate the total balance, income, and expenses. When users add a transaction, JavaScript creates an object containing a unique ID, description, and amount.

Finally, the deleteItem() function removes a transaction using filter(), saves the updated data, and refreshes the displayed information.

// 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();
Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Aayush

Very Nice Desgin and easy for beginner

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.