Appearance
Frontend Architecture
React 18 + Vite SPA, TypeScript-first, code-split by tier, hosted on Cloudflare Pages.
Layered structure
- pages/ — routed screens; compose components, never call Supabase directly.
- components/ — presentational + feature UI; import UI from their tier's
components/ui/. - hooks/ — the read path (
useQuery+rpc). Standard shape{ data, isLoading, error, refetch }. - services/ — the write path (
useMutation+functions.invoke). - shared core — types, Zod schemas, service/data-access contracts, pure logic. No react-dom/DOM imports so web and the future Expo app both consume it.
The Supabase client (one singleton)
- One canonical typed singleton:
src/lib/supabaseClient.ts.@/lib/customSupabaseClient(imported by ~50 files) andsrc/shared/lib/supabaseClient.jsre-export it. - No
createClientanywhere else. No hardcoded production-fallback URL in the client — an E2E/cold-stack retry must never reach a real project.
Design System & UI/UX [ENFORCED — non-negotiable]
Modern-SaaS quality on every screen (web + future native).
- Apple-style soft corners —
rounded-3xlshells,rounded-2xlinner panels. A design invariant. - Zero hard-coded colors in class strings (chart stroke/fill are the only exception).
- Mandatory light + dark theme driven centrally (
cn()+isLight/ tokens) — never per-page branching. - UI Hybrid System: global primitives in
src/components/ui/+ per-tier overrides intiers/{tier}/components/ui/that wrap the global. Consistency invariants (radius, type scale, spacing, focus, motion, elevation) never differ per screen. - Design tokens live in the portable core (so native inherits them).
TypeScript [ENFORCED — TS-first]
.ts/.tsxfor logic; keep UI glue idiomatic. Convert existing.js/.jsxopportunistically during retrofit.tsconfig.jsonis strict butallowJs: true/checkJs: falseduring the transition.- Path alias
@/*→src/*(kept in sync acrossvite.config.js,tsconfig.json,jsconfig.json).
Performance
- Web-Vitals + bundle-size budgets enforced in CI (target).
- Composite-RPC for multi-section screens — avoid ≥3 parallel read RPCs saturating the pool.
- Cloudflare cache-hit >95% on public pages.
- Tiers lazy-loaded; landing + auth eager for instant first paint.
Vite dev-only plugins — leave alone
vite.config.js injects dev-time Horizons error/console/navigation handlers and loads plugins/visual-editor/* / plugins/selection-mode/* only when isDev (in-browser visual editing preview). Inert in production. Don't strip unless the task is about the visual editor. This is also why vitest.config.ts is standalone (does not merge vite.config.js) — to keep these plugins out of the test runner.