The constraints
ยท Constraints
These four apply to every app in the portfolio. They were fixed before any individual app was designed, and between them they decide more about how these things are built than any single design decision.
1. Local first, with no backend at all
No accounts, no server, no analytics in the apps. State goes into one JSON object in localStorage under a key namespaced to the host, versioned, with a migration hook so an old shape never crashes a returning visitor.
What it buys. There is no database to breach, no session to steal, no bill that scales with usage, and no privacy policy to write because nothing is collected. A tool people point at a real CSV export is a tool they will only use if the data demonstrably cannot leave.
What it costs. No sync between devices, no collaboration, and a hard ceiling on data size. It also means the storage failure modes are real and have to be handled: private browsing, blocked storage and quota all fail the write, so every app surfaces that in words rather than losing work silently.
The one place this bites hardest is SQLpad, which takes dropped CSV files. The query text and saved queries persist. The CSV contents deliberately do not. Dropped tables live in memory and in the worker and are gone when the tab closes. That is the correct trade for a tool people point at real exports, and it is a trade, not a limitation.
2. Anything slow runs off the main thread
sql.js compiled to WebAssembly will happily block whatever thread it is on for as long as a bad query takes. On the main thread that is a frozen page.
So the database lives in a Web Worker and nowhere else, and the main thread keeps the parsed CSV. That second half is the part that matters: sql.js has no interrupt, so cancelling a runaway query means terminating the worker and rebuilding it from the copy the main thread still holds. A cancel button that does not cancel is worse than no cancel button, and without the main thread holding the data there would be no way to recover after the terminate.
3. Every rule that matters is a script that fails the build
A rule nobody checks is a preference. Four run on every pull request:
| Check | What it fails on |
|---|---|
npm run lint | Any warning at all, with --max-warnings 0 behaviour |
npm run check:contrast | A text pair under 4.5:1 in either theme |
npm test | Pure logic that stopped agreeing with its tests |
npm run build | A type error, or anything that stops the bundle being produced |
The contrast check is the one that earns its keep. It reads the token file, walks every theme block in it, and tests seven text pairs in each. It runs in about 40 milliseconds and it is the reason a palette tweak cannot ship an unreadable caption.
4. Offline after first load, without serving a stale app
The ship gate says an app has to work with the network off after first load. The HTTP cache alone does not reliably deliver that, so every app carries a small service worker.
The split matters:
- Navigations go network-first. A deploy is picked up the moment the network allows it, and the cached shell is only the fallback.
- Everything else is cache-first. Vite content-hashes the assets and the font files are immutable, so a cache hit on those can never be stale.
Getting this backwards is how a site ships an update that nobody sees for a week.
What these four produce
An app that loads in about 80 kB gzipped over three same-origin requests, works with the network off, cannot leak data it never sends, and fails its own build if a caption drops under 4.5:1. That is not a set of features. It is what falls out of four constraints applied consistently.
The provenance
V-3 recorded
Source control is GitHub, in the bryancalabro org. calabrodesign and its sibling repos live there, and pull requests and Actions run against it. Data held: repo contents, commit history, Actions logs.
Source: the calabrodesign git remote and .github/workflows/. Last verified 2026-09-21.
V-2 recorded
Hosting is Vercel. It serves the calabrodesign site build; vercel.json sets the SPA rewrite and the output directory. Data held: static build output and analytics events (see V-4).
Source: vercel.json and README.md in calabrodesign. Last verified 2026-09-21.