PayTimeHub started as one page - a paycheck withholding calculator - because I couldn't find one online that didn't bury the number under three popups and an email gate. It's since grown into a small family of calculators: paycheck withholding by state, a raise calculator, hourly-to-salary conversion, hours-to-decimal timesheet conversion, and time-and-a-half overtime. Nothing behind a signup, nothing sent to a server. A few things stood out along the way that I didn't expect going in.
The tax logic is the actual product
I assumed the hard part would be the frontend. It wasn't. Federal withholding is a set of published brackets, but state withholding is fifty different rulebooks, several of which don't tax income at all, some of which have their own standard deduction schedules, and a couple that change their brackets more often than you'd think reasonable for a tax table. Getting [state]-paycheck-calculator right per state meant the actual engineering effort lives in a tax engine, not in a component tree. The UI took a weekend. The tax logic is still the thing I go back and double-check.
The lesson: for a "calculator" site, the calculator is the product. Everything else - layout, copy, SEO - is packaging around a number that has to be correct, because a wrong number here isn't a cosmetic bug, it's a wrong paycheck estimate someone might actually plan around.
Floating-point math will find you eventually
Money math in JavaScript looks fine until it doesn't. 0.1 + 0.2 not equaling 0.3 is the famous example, but the version that actually bit me was subtler: a raise calculation that produced $53,999.999999999996 instead of a clean $54,000 on one specific input, not because of an obviously bad float, but because the multiplication itself introduced error just below a rounding boundary. I wrote about that one separately, but the broader takeaway was that every calculator that touches money needs to funnel through the exact same rounding function, or you end up with five slightly different .toFixed(2) calls that quietly disagree with each other on the edges.
Static-first is a feature, not a shortcut
Astro was the right call, but not for the reason I expected going in. I picked it for the build output - static pages, minimal JS, fast Lighthouse scores. What turned out to matter more is that "no server, no data leaves the browser" isn't just a privacy pitch, it removes an entire category of things I don't have to build: no auth, no database, no rate limiting, no GDPR data-handling story. The calculators run entirely client-side. That constraint made the whole project smaller in a good way - there's less surface area for something to go wrong, and less to maintain six months from now.
Dynamic routes make "fifty states" tractable
Before I set up [state]-paycheck-calculator and [value]-salary-to-hourly as dynamic routes, I was mentally treating "add all 50 states" as fifty separate pages to hand-write. That's the wrong mental model for this kind of site. One template plus a data file per state is the actual shape of the problem, and it's the difference between a task that takes a weekend and one that never gets finished because it feels too big to start.
Shipping something narrow beats shipping something broad
Early on I was tempted to build a general "financial calculator suite" - retirement, mortgage, budgeting, the works. I'm glad I didn't. Payroll and pay math is a coherent enough niche that the calculators reinforce each other: someone converting hourly to salary is plausibly also the kind of person who wants to know their overtime rate or their raise in real terms. A broader suite would have diluted that, and I'd have shipped ten shallow tools instead of five that actually do their one job well.
What's next
More state-specific accuracy passes, and probably a couple more calculators in the same vein - the goal isn't to cover every financial calculation that exists, just to keep doing pay and payroll math well. If you're curious, the whole thing is live at paytimehub.com.
Top comments (0)