← Lab

Designer & builder 2026

RVA Miles

RVA Miles is a work-mileage tracker for someone who drives for a living and files for reimbursement every pay period. A repeat trip is one tap, payday is three, and nothing that touches money is ever recomputed.

  • Product design
  • Content design
  • Offline-first
Cover reading "One tap". Three tasks as rows of tap dots: a repeat trip is one tap and carries the accent, a new route is three, payday is three.

What it is

I built RVA Miles around one person’s actual week. The driver goes to the same handful of places, logs each trip without thinking about it, and on payday sends the manager a report that looks like it came from an accounting department. Trips, routes, and settings live in IndexedDB, the browser’s own database, on the phone that logged them. There is no account and no server in normal use.

How it works

The app is a trip logger with memory rather than a GPS tracker. Live tracking is there for a route the driver hasn’t taken before.

Home. Under a "This pay period" card reading 116.2 mi and $84.24, a violet banner asks "Missing anything?", and below it a two-column grid of route tiles — Short Pump Office 19.6 mi, Scott's Addition depot 4.1 mi, Chesterfield Clinic 14.2 mi — each labelled with how often it was driven this month.

The catch-up sheet, open over the home screen. It reads "Missing anything? Your usual trips for days with nothing logged", then a dated group, Friday Aug 28, offering Chesterfield Clinic at 14.2 mi, and a dashed "Something else" row.

Report. A gradient-hairline card gives the period Aug 20 to Sep 2, 116.2 mi and $84.24, then 12 trips at $0.725 a mile, the driver, and the vehicle. Below it a "Share report" button and a note that the link contains the report data itself.

The live drive screen: a near-black stage with a single violet ring around a START disc, the vehicle name beneath it, and a card reading "Leave this screen open while you drive — GPS stops when the app is in the background."

The four screens are logging a trip, recovering a missed day, sending the report, and the live-tracking fallback. All of them run on the seeded demo ledger, which the app labels in the pill at the top of every screen.

One tap

Home is a grid of routes ranked by how often each one is driven and whether it fits today’s weekday. Tapping a tile logs the trip and offers an undo. There are no confirmation dialogs anywhere in the app, and deletes are soft, so undo puts the trip back. Logging a trip and saving its route are the same action, so an address never gets typed twice.

Missed days don’t have to be typed back in either. A banner offers the driver’s usual trips for each empty weekday, with the dates already filled in. The banner appears only on evidence from two separate dates for that weekday, because four trips logged on one Tuesday are not a weekly habit.

The money is a fact

Everything that touches money is captured when the trip is logged and never recomputed. The rate is copied onto the trip at that moment, so changing it in settings cannot rewrite what is already filed. Each trip is rounded and the rounded values are summed, because 14.2 miles at 72.5 cents is 10.295, which JavaScript’s toFixed renders as $10.29 while a correct sum reports $10.30. Accounts payable reconciles that penny by hand, so a one-cent disagreement between the app and the report it sent becomes a support ticket.

Dates get the same treatment. Every date is a local YYYY-MM-DD key captured at logging, and nothing parses a calendar date as UTC midnight, so a pay period includes its final day and a 9pm trip isn’t filed under tomorrow. Published IRS rates are quoted in half cents, so the rate has its own formatter. If 0.725 prints as $0.72, miles times the printed rate no longer matches the printed total.

Sending the report

The report goes out as a link that carries its own data, gzipped into the URL fragment, which browsers never send to a server. The manager opens a document, prints it, or downloads the spreadsheet. There is nothing to install and no account to make.

How I built it

I wrote the first version in a day in January, and it was wrong in ways I only found once someone leaned on it. A reload mid-trip zeroed the miles while a hardcoded “TRACKING ON” badge still reported the drive as recording. The mileage rate was applied at export time instead of captured per trip, so changing the rate rewrote history. Every export dropped the final day of the pay period, because the code parsed a calendar date as UTC midnight. Storage quota errors were caught and discarded, so the app said “Trip saved” over a write that never landed.

In August, I ran an eight-way adversarial review against the source, confirmed the correctness claims, and rewrote the app in a day. Live tracking survived the rewrite and now reports its own state accurately, and the route grid became the home screen.

Two demo ledgers ship behind a URL flag, each on its own database so neither can reach the real one. The clean ledger is ten weeks of a plausible week, with the pay period part-elapsed and two gaps for the catch-up banner to find. The second ledger is deliberately hard to display: nineteen months, about 150 trips, a 60-trip month, a 130-character place name, four identical trips on one day, a journey where nothing was billed, and a purpose beginning =SUM( to prove the CSV writer neutralizes it. Every record in it is valid, and every record is the kind of thing that makes a screen look bad. I made most of the layout decisions against that ledger.

What I learned

Money and dates set the rule I would carry into other work. A value that someone will act on gets captured at the moment of the event and is never recomputed, because a number that moves after the report has gone out becomes a support ticket.

The adversarial ledger is also the limit of what I have tested. The quality bar I wrote for the rewrite named a browser smoke test of the six core flows on a phone, and that test doesn’t exist. The two-device sync test that does exist runs against a mock key-value store on my machine and isn’t in continuous integration.