Skip to content

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) and src/shared/lib/supabaseClient.js re-export it.
  • No createClient anywhere 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 cornersrounded-3xl shells, rounded-2xl inner 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 in tiers/{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/.tsx for logic; keep UI glue idiomatic. Convert existing .js/.jsx opportunistically during retrofit.
  • tsconfig.json is strict but allowJs: true / checkJs: false during the transition.
  • Path alias @/*src/* (kept in sync across vite.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.