Appearance
Frontend Architecture
React 18.2 + Vite 4.4, JSX only (no TypeScript). Tailwind for styling. The import alias @ → src/.
State strategy
| Concern | Approach |
|---|---|
| Server reads | Custom hooks (useState + useEffect + useRef) calling supabase.rpc(), cached via @/lib/cache. Not TanStack Query. |
| Mutations | TanStack Query useMutation (used for EF write calls, primarily in Edge Builder). |
| Forms | React Hook Form 7.49. |
| Theme | useTheme() → { isLight }, applied with cn(). |
| Auth | useAuth() from SupabaseAuthContext. |
| Feature flags | useFeatureGate() from FeatureGateContext. |
Component rules
- Default exports for page/feature components; named exports for shared UI primitives.
- Props destructured in the signature — never
props.xin the body. - No data fetching in components — always delegate to a hook.
- No
supabase.*calls in components — they are pure UI consumers. - Import UI from your tier's
components/ui/, not globalui/directly.
Hook rules
One data concern per hook (one RPC per hook). Return the consistent shape { data, loading, error, refetch }. CACHE_KEY is a module-level constant. A hasLoaded ref flips loading to false only once, so background refreshes don't flicker the spinner. See the canonical pattern in Market Mood and Data Access Strategy.
Routing
Routes are defined as route objects in src/components/routes/AppRoutesConfig.jsx (and AdminRoutesConfig.jsx), with pages lazily imported via React.lazy. The full path → component → source mapping is auto-generated: Route Map.
UI Hybrid System
Two layers:
src/components/ui/ ← GLOBAL PRIMITIVES (tier-agnostic, zero tier logic)
src/tiers/users/components/ui/ ← USER overrides (orange/dark theme)
src/tiers/admin/components/ui/ ← ADMIN overrides (blue/light theme)
src/tiers/public/components/ui/ ← PUBLIC overrides
src/tiers/landing/components/ui/ ← LANDING overrides- Feature components import from their tier's
components/ui/. - Tier overrides wrap the global primitive:
import { Button as GlobalButton } from '@/components/ui/button'. - Consistent across tiers (never tier-specific): border radius, typography scale, spacing, focus states, animation timing, elevation.
- May differ per tier: primary color, light/dark theme, hover intensities, branding.
Theming (component-first, mandatory for new code)
Every component handles dark and light mode with the cn() + isLight guard:
jsx
import { useTheme } from '@/contexts/ThemeContext';
import { cn } from '@/lib/utils';
const { isLight } = useTheme();
className={cn('base', isLight ? 'bg-white text-gray-900' : 'bg-slate-800 text-white')}Never hardcode hex colors in class strings (charts are the only exception). Never import the legacy @/theme/lightTheme / darkTheme. The full design system lives in documentations/01-layout/guidelines/niftytoolkit-uiux-theme-guidelines.md.