PWA and mobile
A space is an app. Not a separate mobile build, not a wrapper bolted onto a website — the same space you publish is installable on a phone, opens from the home screen, and stays useful with no signal. CTXR ships this as a Progressive Web App, and it fits so naturally because the hard part was already solved upstream.
This page covers the delivery layer: install, navigation, and offline. What the installed app can do with the device — location, notifications, and the behaviours built on them — lives on its own pages: Geolocation, Notifications, Proximity.
Every URL is the app
There’s no special /app address to install. The page you’d send a friend — yourspace.ctxr.app/some-route — is the installable surface: a real, crawlable, shareable URL and the entry point of the app at the same moment. Install it and it opens straight to your content. Move around inside it and navigation feels immediate, pages changing without a full reload, while every page stays a genuine URL you can link to, bookmark, or share.
This is deliberate. The live site and the installed app are the same thing at the same address, rather than a public website plus a separate “app version” kept in sync. Editing lives on its own addresses, kept entirely out of the installable product, so what a visitor installs is only ever the published site — never a draft, never the editor.
Two surfaces, one machinery
Two things are installable. A space site can be added to the home screen, so a visitor lands directly in that space’s content. The account app — the cross-space surface a person carries with them — is installable too, holding the spaces and folders they follow. Different entry points, the same underlying machinery: a manifest, a service worker, and a small local cache. There’s no separate “mobile build” to maintain alongside the web one.
Why it fits
The live product is already compiled JSON sitting on disk. That’s not a coincidence we’re exploiting after the fact — it’s the compiled graph doing its job. A page is a self-contained document with everything it needs already resolved, which means it is already offline-grade raw material. Caching it for offline use is storing a file, not reconstructing a render. Account snapshots — the small set of spaces and folders a person follows — are likewise small and local. Nothing about going offline asks the system to do something it wasn’t already doing.
Compiled page (JSON on disk) ──┬─ served online, fresh
└─ cached locally → served offline, stale-but-visible
The same goes for feeds — small public extracts the platform keeps current, like the coordinates of every published place. They’re plain JSON files served like everything else, cached like everything else, and that’s what lets a behaviour such as Proximity keep working offline.
Offline means visibly stale
Offline is honest about itself. You see the pages and folders you last looked at, each carrying a badge that shows its age — not a blank screen, not a silent lie that the content is current. When the connection returns, the view refreshes and the badge clears. Stale-but-visible beats fresh-but-absent, as long as the staleness is never hidden.
Dependency-free, like the rest
The whole PWA layer follows the same rule as the core: no external packages, no build pipeline, nothing to drift. A manifest, a generated service worker, a precache of the space’s own assets — all produced by the platform itself. The payoff is consistent with file-based and dependency-free: less attack surface, a simpler deploy, and an installed app that is exactly the website, nothing more.