Frontend Svelte Implementation Plan
tracker-frontend-svelte is the Svelte 5 rewrite of the customer-facing
dashboard, built side by side with the old React app until it reached parity.
The migration is complete — the React app (tracker-frontend) has been
deleted and this is the live app. This doc now records the architecture
decisions that are still true, not the original migration plan. For the
cutover history (phases, incidents, dates), see
svelte-staging-cutover-todo.md.
Code Style
Written for Svelte 5 runes only, no Svelte 4 legacy syntax (export let,
$:, on: directives). Components stay small and composable; shared UI and
state logic live in src/lib/components/ or src/lib/utils/ once reused
more than once rather than being duplicated per page.
Repo Shape
tracker-frontend-svelte/
├── build/
│ └── dev/
├── public/
├── src/
│ ├── lib/
│ │ ├── api/
│ │ ├── components/
│ │ ├── theme/
│ │ └── utils/
│ ├── routes/
│ ├── app.css
│ ├── app.html
│ └── main.ts
├── package.json
├── tailwind.config.ts
├── vite.config.ts
└── svelte.config.js
Kept self-contained (own build, own dev container) so it could be developed and deployed independently of the React app until cutover.
Docker
Runs as its own Compose service (frontend-svelte-dev, build context
./tracker-frontend-svelte, build/dev/Dockerfile), depending on the API dev
service rather than the old React frontend.
Theme System
A curated daisyUI theme setup (not a single dark/light toggle), following the Cortex theme-switcher pattern, in three layers:
tailwind.config.tsdeclares the allowed daisyUI themes.app.htmlapplies the initial theme (read fromlocalStorage, falling back toprefers-color-scheme) before Svelte mounts, to avoid a flash of the wrong theme.src/lib/theme/*+ThemeSelector.svelteresolve/persist the active theme and expose the curated list to the selector, which lives in the app shell so every page gets the same controls.
DRY Structure
- Shared form controls are reused across login, locations, campaigns, and reports.
- Map controls are separate from map page containers.
- Modal shells (
AnimatedDialog) and table/pagination primitives (Pagination.svelte,SortableHeaderButton.svelte) are centralized rather than reimplemented per feature. - Theme logic lives in one module and one selector component.
Pages that grow large split by responsibility (page container / data loading / state and derived values / presentational components / feature-specific controls / API adapter) rather than staying monolithic — see frontend-svelte-dashboard-refactor-todo.md's history for how this was applied to the dashboard once it grew past ~800 lines.
Leaflet
Leaflet itself was kept; only the React wrapper layer was replaced. The Svelte version uses direct Leaflet integration with Svelte lifecycle hooks for map setup/teardown, and small wrapper components for reusable map UI.