● Kanton Zürich · 100 % on-device · iOS
A real-time tax reserve, Swiss QR-bill invoicing and receipt scanning for sole proprietors — designed, built and shipped solo.
01 — The problem
Every self-employed person in Canton Zürich pays into five separate systems — each with its own rates, brackets and municipal multipliers. Most freelancers guess how much to set aside, and guess wrong.
Invoicing, expense tracking and tax estimation each live in a different tool — or a spreadsheet, or a Treuhänder. Nothing combines all three affordably, and nothing does it privately.
02 — The solution
The Solaris orb is the heart of it: a live tax reserve that recalculates with every invoice and receipt, across 25+ Zürich municipalities. It glows teal when you're fully covered — tax awareness becomes passive.
Real-time reserve calculator covering AHV/IV/EO (incl. sliding scale), cantonal, municipal, federal and church tax for 25+ municipalities.
Scan your provisional Steuerrechnung, compare it against actual year-to-date income, and auto-generate a formal Einsprache in German if you've been overcharged.
Swiss QR-bills to the SIX SPS v2.3 standard in one tap. Mark an invoice paid and the revenue flows straight into the tax calculation.
Photograph a receipt; on-device OCR extracts merchant, amount and date. Deductions update the reserve instantly.
03 — Design identity
Near-black canvas, one hero accent, and a strict typographic rule: when you see monospaced text, it's money.
The orb doubles as data display and emotional indicator. Colour went through three iterations — the shift from navy to near-black came from seeing it on a real device, not from a prompt. The app ships in both light and dark mode.
04 — Architecture
No servers, no cloud database, no accounts, no analytics SDKs. Everything runs on-device with SQLite — so the App Store privacy label reads “Data Not Collected.” Almost no finance app can claim that.
The tax engine is pure TypeScript with zero dependencies, validated by 29 unit tests. Privacy wasn't a compromise; it's the positioning.
05 — The hardest bug
The QR-bill passed every internal test — then Raiffeisen's validator rejected it against three spec violations. Fixing it meant restructuring the address model across settings, onboarding and invoice creation, plus a pre-flight gate that blocks a broken bill before it ever reaches a client.
This is the detail that proves the app was built against a real standard, not a mock.
Creditor address incomplete — combined address lines rejected; structured street / building / postal / town required.
Debtor address incomplete — same structural requirement on the payer side.
Invalid reference type — SCOR with a non-RF string on a normal IBAN.
Structured address model across settings, onboarding and invoicing · QRR for QR-IBANs, NON for standard IBANs · pre-flight validation gate before export.
06 — Three bugs worth writing down
The interesting problems in a local-first app aren't API design — they're the seams between navigation, persistence and rendering. These were the three that fought back.
React Navigation v7 moved scene background styling to sceneStyle, and the default isn't transparent — so a correctly rendered dark background sat under an opaque white layer the navigator injected on every screen. The fix was one line per screen, sceneStyle: { backgroundColor: 'transparent' }, but finding it took real diagnosis: the failure reads exactly like a broken theme, not a navigator default.
Zustand's persist middleware rehydrates from AsyncStorage asynchronously, but components render immediately against empty initial state — so first paint showed a blank app before data popped in, and a write racing the rehydrate could clobber stored data. An explicit hasHydrated flag, set via onRehydrateStorage, gates the UI on hydration completing instead.
The expense tab loaded data in useEffect, which fires on mount — not on focus. Tab screens stay mounted, so returning after scanning a receipt showed stale data until useFocusEffect tied the refresh to navigation focus instead. The class of bug matters more than the fix: local-first apps don't get cache invalidation for free from refetching a server.
07 — Process
Claude for strategy, tax-law research and architecture. Pencil.dev for twelve wireframed screens. Claude Code for implementation. Expo and Xcode for on-device testing.
Scope was deliberately cut from four cantons to one — Zürich with verified, accurate data beats four done with guesswork. AI moved implementation at high speed, but every product decision, design judgement and quality bar came from a human reviewing it on a real screen.
08 — Shipping is its own discipline
App Review returned two citations on the first submission — both closed in a single cycle. The fix exposed something larger: a screenshot pipeline that could hide bugs no reviewer would ever be positioned to catch.
The screenshot set was hand-composited in Affinity binaries. Auditing it for the citation surfaced two latent bugs shipping in the pixels — HTML entities leaking as literal text (— and · rendering as “mdash;” and “middot;”), and English and German captions concatenated into a single string. Rather than patch binaries that can't be diffed or reviewed, the set was rebuilt as a data-driven pipeline — typed per-locale caption objects, an HTML template, a headless-Chromium renderer. The bug class is now structurally impossible.
Pre-permission screen read as consent — a “Grant Permission” button, which Apple treats as directing the user to consent. Relabelled to “Continue.”
“Free” appeared in a screenshot — Apple counts references to free service as price references in metadata. Removed.
Outcome
Live on the App Store · marketing site live at taxmeter.ch · full custom screenshot set in both languages.
Reflection
“AI writes the code. Humans make the decisions.”
This project tested whether a designer could ship a production-quality native app, a marketing site and a compliant Swiss financial tool solo, using AI tools — all the way to the App Store. The answer was yes, with a caveat: the value wasn't the code generation. It was the product thinking, the design judgement, and the willingness to test against reality — a real bank, a real device, a real tax table.