Frontend structure
App Router
Each app (apps/tracking, apps/portal) is a Next.js App Router project under
src/app/:
page.tsx— login gate.layout.tsx— root layout. Importsglobals.css, mounts the prefetch<XLoader/>components from@enode/core, and runs a boot script that addsclass="ios"to<html>on the Capacitor iOS build.- Feature routes — composed from co-located
use-*hooks and presentational files.
Styling & design tokens
Tailwind v4 with the Enode design system in
packages/ui/src/styles/design-system.css. Each app's globals.css composes it:
@import "tailwindcss";
@import "@enode/ui/styles/design-system.css";
- Use semantic colour utilities (
bg-surface-base,text-foreground-muted,border-surface-stroke,bg-foreground-red-orange, …) and type-style classes (heading-hero,body-normal,stat-large-digits, …). - Never raw
gray/orange/emeraldutilities. Tokens carry light + dark values and flip viaprefers-color-scheme, so don't adddark:variants. - Destructive states still use raw
red-*— the design system has no danger token (a known gap). ios:is a custom variant scoped to the Capacitor iOS build.
Conventions
- State resets adjust state during render (the
prev-value pattern), not viasetStateinuseEffect— ESLint enforces this. - Local entity ids are
crypto.randomUUID(). - Reusable presentational components belong in
@enode/ui; shared non-UI logic in@enode/core. App-specific views stay in the app.
See Components for the reusable component layer and Storybook.