What Collos is — and what it is not
Collos is a care-coordination app for the person in a family who has become the organiser. It turns check-ins, a water break, a walk and an evening note into one short daily plan, and it remembers what has already been done so the next person does not have to ask.
It is deliberately narrow. Collos:
- holds no medical records, no diagnosis and no clinical claims;
- does not replace a qualified care professional, and says so in the app;
- does not sync between phones yet — the plan lives on the device in front of you;
- does not ask for an account, because there is nothing to log in to.
Quick start
Open collos-app.vercel.app in a browser, or run the app on a phone with Expo Go. Either way you land in onboarding:
- Tap Set up my care circle, then say who you are caring for. The app creates one person and hands you their plan.
- That first plan is empty on purpose. Add a moment by hand, or tap Start from a template for the four everyday ones — all open, so you only tick what actually happened.
- Add or switch between the people you care for at the top of the Today screen.
- Confirm a moment, or skip it. Both are recorded the moment you tap.
- A skipped moment is not a lost one: it offers Move to tomorrow in one tap, the plan header says how many left the day, and on the day it moved to the moment comes back wearing a Moved here chip.
- Tap Shared notes to leave a note for whoever picks the plan up next.
Settings holds the store connection details, a restore button, and Reset all data on this device if you want a clean slate.
Where your data lives
Everything you enter — confirmations, skipped moments, added moments, notes and the recipient you last viewed — is written to device-local storage immediately, and reloaded when the app opens.
src/storage/careStore.ts the state itself: load, save, transitions
src/storage/fileStore.ts native: expo-file-system
src/storage/fileStore.web.ts browser: localStorage
There is no backend. Nothing you type is uploaded, which is why the privacy policy can make the promise it makes. The single exception is a subscription: RevenueCat receives an anonymous app user ID and the purchase, and nothing else.
Notes follow the same rule. The free plan keeps one note a day for each person, and Pro removes that limit — but a note is stored locally either way, so the difference is a cap, not a sync.
Plans and purchases
| Plan | Price | What it changes |
|---|---|---|
| Free | $0 | The whole daily plan, every moment, adding your own moments, and one shared note a day for each person. |
| Collos Pro | $4.99 / month $39.99 / year |
Removes the shared-notes cap. As many notes as your circle needs. |
Pro is billed by the store you bought it in: the App Store, Google Play, or RevenueCat
Billing when you subscribe in a browser. The public web build carries a
test_ key, which routes purchases through RevenueCat's Test
Store: a real RevenueCat transaction that appears in the dashboard, with no
money moving, and the app names it honestly on its Settings screen instead of
pretending to bill a store it is not using. Your store shows the price in your own
currency, and cancelling happens in that store account — not in Collos.
Restoring a purchase
Open Settings and tap Restore purchases. Restore is available on iOS and Android; it is not part of the web build, where a purchase is tied to the browser profile that made it. The same screen shows the app user ID, the entitlement identifier and the RevenueCat project ID, so support can find the record if a restore ever fails.
In a development build
A purchase inside Expo Go uses JavaScript-level mocks: the flow is complete and clearly
labelled, but no money moves. Real store purchases need a development or production
build, which is what eas.json is for.
Run it yourself
git clone https://github.com/thesithunyein/collos
cd collos
npm install
npm start # Expo dev server: i, a, or scan the QR code
npm run typecheck # tsc --noEmit
npm run build:web # the browser build, written to dist/
Subscriptions are optional locally. With no RevenueCat key present the app runs in a clearly labelled preview mode: every screen works, prices are shown, and no payment is taken.
| Variable | Purpose |
|---|---|
EXPO_PUBLIC_REVENUECAT_IOS_KEY |
App Store purchases |
EXPO_PUBLIC_REVENUECAT_ANDROID_KEY |
Google Play purchases |
EXPO_PUBLIC_REVENUECAT_WEB_KEY |
Browser purchases through RevenueCat Billing |
EXPO_PUBLIC_REVENUECAT_ENTITLEMENT |
collos_pro — must match the RevenueCat dashboard exactly |
EXPO_PUBLIC_REVENUECAT_PROJECT_ID |
The proj… id, shown in the app's Settings screen |
All five are EXPO_PUBLIC_* values and public by design. They are documented
in .env.example; copy it to .env and fill it in.
How it is built
One React Native codebase, two output targets. The native app and the browser build share every screen; only the storage primitive differs per platform.
App.tsx app shell: stage, tabs, sheets, persistence wiring
src/screens/ Onboarding, Today, Circle, Settings
src/components/ TaskCard, NavItem, NotesSheet, AddMomentSheet, PaywallModal
src/storage/ device-local state, split native vs web
src/purchases/ the only place the RevenueCat SDK is imported
src/theme.ts colour, radius scale, safe-area insets
-
Expo SDK 57, React Native 0.86.3. Identifiers are
com.collos.appon both iOS and Android, and EAS builds both fromeas.json. -
Dependencies are pinned, deliberately.
expo-fontis held at~57.0.4with a matchingoverridesentry, because a floating version resolves to a release that callsregisterWebModuleand blanks the web build at runtime. The override has to move whenever the SDK does — npm refuses to install while the two disagree. -
The entitlement gates one thing. Notes are capped in
careStoreand the cap is lifted when the RevenueCat entitlement is active. Nothing in the paywall promises anything the code cannot do.
Screenshots and assets
Every phone frame on the product page is a capture of the running app, taken by the script below rather than drawn by hand — the hero included. That is deliberate: the hero used to be a hand-built impression of the plan screen, and it drifted, still advertising a percentage the app had stopped showing. A capture cannot drift.
Four captures are used, one per screen, and no screen appears twice: the dashboard is the hero, the dark band shows setup and the editor for a moment, and the showcase shows the circle. The only hand-laid-out parts of that composition are the two annotation cards beside the phone, and they carry the app’s own copy — the evening note’s title and its prompt are strings from the build.
What the phone stands in is light rather than a shape: a slow sweep of the brand blues
that takes 84 seconds to turn once, two accent blooms, a dot field, a pool of brand
light that follows the pointer, and one pass of SVG grain over the whole hero. The
grain is doing a job, not a mood — a gradient this large bands on a cheap panel,
and banding is the clearest tell of a gradient that was stepped rather than
photographed. It replaced two turned blue rounded squares, which were the third
attempt at a shape behind the hero and still read as a graphic, because a hard-edged
slab of flat colour behind a photograph of a device always does. All of it is
decoration in the strict sense: it is behind aria-hidden, nothing in it
is needed to read the page, and under prefers-reduced-motion every clock
stops, the light holds still, and the pointer listeners are never attached at all.
npm run screenshots # the two previews used on the landing page
npm run screenshots:devpost # 1179x2556, the submission size
npm run screenshots:appstore # 1320x2868, the 6.9-inch set
npm run screenshots:playstore # 1080x1920
Every icon and favicon is generated from one source image by
python assets/make-icons.py, so the app icon, the Android adaptive
foreground, both favicons and the splash mark cannot drift apart.
The social preview card is built the same way, by
python assets/make-og-image.py: 1200x630, the hero's own gradient,
and the same dashboard capture inside the app's navy bezel. It exists because a
link preview crops to about 2:1, and a portrait phone screenshot served to one
arrives as an unlabelled middle slice of a screen.
Troubleshooting
| Symptom | Cause |
|---|---|
| Blank white page in the browser build |
A floated Expo dependency. Keep expo-font pinned and move
expo, expo-font and @expo/vector-icons
together if you upgrade.
|
| The paywall says “Preview” in a build where keys are set |
The variable is missing from that environment. EAS does not read a local
.env; set the variables per environment and rebuild.
|
| A purchase succeeds but Pro stays off |
The entitlement identifier does not match. It must be collos_pro
unless EXPO_PUBLIC_REVENUECAT_ENTITLEMENT says otherwise.
|
| Plans never load | No default offering, or packages missing from it. |
| The hero’s glow drifts away from the device instead of sitting behind it |
A transform: translate(-50%, -50%) on an element that also animates
rotate or scale. The individual transform properties
compose with transform rather than replacing it, and
transform is applied innermost, so the centring offset is rotated
along with everything else and the layer orbits its own anchor. Centre with the
translate property and leave transform to carry only
the movement.
|
Support
Questions about a purchase, a bug, or something this page does not answer: email sithunyein.mailto@gmail.com, or open an issue on GitHub. Include the app user ID from Settings if it is about a subscription — it is the fastest way to find the record.