One thing that never gets easier might be splitting the bill at restaurants. Not because the math is hard, but because everyone has opinions…who ordered the expensive drink, should we tip on tax or before tax, why is the suggested tip calculated on the total with tax already added?
So I built something.
Here’s the link to the Tip Calculator, please try it out and let me know what you think π
The Idea
I wanted something dead simple: open it on your phone at the table, punch in a few numbers, and know exactly what everyone owes. No app download, no sign-up, just a webpage.
The features I had in mind:
- Two modes β split evenly by person, or split by what each person ordered
- Shared dishes β if three people shared the dumplings, split it three ways
- Tax + tip comparison β show the tip amount both before and after tax, so you can see how much the restaurant is quietly inflating the suggested tip
- Auto tax rate β detect location and fill in the local rate automatically, because nobody knows their city’s exact restaurant tax off the top of their head
The Stack
Single index.html file, HTML, CSS, and vanilla JS all inline. No framework, no build step, no backend.
I used Cursor for this one, mostly as an experiment in AI-assisted workflow. I could’ve written this by hand, but vibe coding is something I’d trying to work and and I wanted to see how much faster the iteration loop gets when you let the model handle boilerplate and first drafts.
For deployment I went with Vercel over GitHub Pages. Pages requires the repo to be public, Vercel works fine with private repos. Free subdomain is cleaner too.
The Fun Part: Debugging
This is where AI-generated code shows its cracks.
The input focus bug was the most frustrating. In the “split by dish” mode, typing a price like 122 would only save 1. The input was losing focus after every keystroke. The state update was triggering a full re-render and unmounting the field mid-input. The fix was straightforward once I spotted it (stabilize the key so the component doesn’t remount), but Cursor had generated the problematic pattern in the first place. Classic case of the model producing code that looks fine but has subtle lifecycle issues.
Tax rate localization was fiddlier than expected. The flow was: GPS β Nominatim reverse geocoding API β match against a hardcoded city/state tax table. The issue was string matching, Nominatim returns inconsistent city name formats depending on zoom level and region. “New York City” vs “New York” … Ended up matching on both city and county fields with a fallback chain.
The tip comparison card overflowed on narrow viewports. Two-column layout didn’t hold below ~360px, switched to stacked.
General takeaway: the AI is good at structure and boilerplate, less good at subtle state management bugs. You still need to read the output and try a few workarounds, etc.
Design Choices
Went through a few color directions. Started with orange, scrapped it immediately… felt like a badly designed food delivery app. Landed on muted sage green. For the results section I used white cards with a green left border accent on a light green background, creating a clear visual layer between input area and output results.
Added localStorage persistence so inputs survive accidental tab closes. For analytics I used GoatCounter, no cookies, no personal data, easy to add in. I don’t need a full funnel, just a rough headcount.
What It Does Now
- Split evenly or by dish, shared items split among a selectable subset of people
- GPS β Nominatim β local restaurant tax rate, with manual state selector fallback
- Pre-tax vs post-tax tip comparison with savings delta
- Copy bill as plain text for group chats, screenshot export
- Bilingual (Chinese/English) based on device default
- Mobile-first,
localStoragepersistence, one-tap reset
Reflections on the AI-Assisted Workflow
Cursor drastically sped up the parts I find tedious. For those, just describing what I wanted was faster than writing it.
Where it fell short was anywhere requiring actual reasoning about the code: the focus bug, the geocoding fallback logic, layout edge cases on small screens. Sometimes it would confidently produce something plausible that didn’t quite work. Having the background to read the output critically and know where to look made a big difference… I imagine this workflow is a lot more frustrating if you can’t.
Would I use it again? Yeah sure, for the right tasks. It’s a good first-draft machine. Just don’t skip code review if you’re overly confident.