Vibecode build plans · № 4
Build an invoice generator
One file, no signup, no SaaS — a freelancer fills in the fields, hits print, and gets a clean PDF. A plan you can paste into your agent, with the money traps marked.
Every freelancer eventually rents an invoicing tool — twelve dollars a month to turn a form into a PDF, plus an account, plus your client list living on someone else's server. An invoice is a form and some arithmetic. You can build the whole thing tonight as one HTML file that runs from your desktop, keeps your data on your machine, and prints to PDF with the button your browser already has.
This plan is opinionated, because invoices are where "close enough" gets expensive. It steers your agent away from the failure modes that actually cost you — money that's off by a cent, a PDF library nobody needed, a printed page covered in buttons — and toward the two things that matter: the totals are exactly right, and the printout looks like a real invoice.
The spec — and the cuts
- One HTML file. Inline CSS, no build step, no framework. A form for sender + client + line items, a live-updating invoice, and a print button. That is the entire application.
- The math is the product. Quantities × rates, a subtotal, an optional tax line, a total — all correct to the cent, every time. If the numbers can't be trusted, nothing else matters.
- Cut: accounts and login. It's your invoice on your machine. There is no one to authenticate.
- Cut: emailing or "sending". You print to PDF and attach it to your own email. Building an SMTP path is a week of work to replace a drag-and-drop.
- Cut: payment processing. An invoice is a request for money, not a checkout. Stripe belongs in a different app.
- Cut: a database. localStorage remembers your details and your client list. That's the whole persistence layer, and it's plenty.
The prompts
Tick the features you want — the prompts below rewrite themselves. Then paste them into Claude Code (or Cursor, or any agent) one at a time, in order.
Every toggle edits the prompts. The copy buttons grab the current version.
1 · Build it
2 · The money-math + polish pass (don't skip this one)
3 · Wire up print-to-PDF
4 · Get human eyes on it before you send it
Where agents go wrong on this build
- Doing money math in floating point.
0.1 + 0.2is not0.3, and on an invoice that's a client emailing you about a rounding error. Compute everything in integer cents — multiply, sum, apply tax as integers — and format to two decimals only at the very end, at display time. - Reaching for a PDF library. Left alone, agents will npm-install jsPDF or pdfmake and reinvent layout in canvas coordinates. You don't need it. The browser's built-in "Save as PDF" plus a print stylesheet produces a cleaner, selectable, real-text PDF for zero bytes of dependency.
- Forgetting the print stylesheet. Without an
@media printblock, the printed invoice carries the form fields, the buttons, the toolbar, and the page background straight onto the paper. The screen and the printout are two different documents; the print rule hides everything that isn't the invoice. - Invoice numbers and dates that reset. If the number is hardcoded or randomised on every reload, two invoices ship with the same ID — an accounting mess. Persist a counter in localStorage that increments per invoice, and format the date explicitly, not with a raw locale string that renders differently on your client's machine.
Why the feedback step is in the plan
An invoice is one of the few things you build that goes straight to someone who's about to pay you — and you only find the typo in your own IBAN, the wrong tax rate, or the client's misspelled company name after you've hit send. You stop reading your own invoice accurately the moment you've stared at it for ten minutes. Prompt 4 wires your agent to our MCP server: it shares the live invoice, you send the link to a co-founder or a bookkeeper, they pin notes right on the numbers from their phone (no login), and your agent reads the notes back and fixes exactly what they flagged. Cheaper than a client correcting you.