Skip to content
appsbynate
← productivity

Budget

A budgeting app that runs entirely in the browser, wearing early-2000s corporate chrome and a Frutiger Aero skin.

Section
productivity
Year
2026
Role
Solo project
Stack
Next.js, React, TypeScript, IndexedDB

Summary

A budgeting app that runs entirely in the browser. Import a CSV, file each row, set a limit per category, watch the month. Transactions, categories and limits live in IndexedDB behind an early-2000s corporate skin — no account, no network, JSON backup in and out.

Story

The design work was mostly picking representations that can’t drift: money as a signed integer of cents, dates as YYYY-MM-DD strings and months as YYYY-MM, so sums are a reduce and nothing loses a day to a timezone. The bugs that mattered all passed the type checker. A positive amount filed against an expense category is a refund, not income, and the first build counted it both ways — one $125 refund moved the net by $250 and made the summary tiles disagree with the donut chart beside them. “Copy limits from July” cleared August before checking the source had anything in it, so a button labelled copy was a way to lose a month with no undo. And semantic colours kept losing cascade fights to more specific base rules, which a contrast sweep turned into a full audit — income green was sitting at 2.77:1.

Lessons

  • Type checks don’t catch semantics. Every one of the real bugs compiled cleanly; only writing the rule down once — income category is income, expense category is a refund — settled it.
  • A destructive step has to validate before it destroys, not after.
  • A semantic colour needs separate tokens for text and for fill, or it passes contrast in one role and fails in the other.