Skip to content

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:

  1. tailwind.config.ts declares the allowed daisyUI themes.
  2. app.html applies the initial theme (read from localStorage, falling back to prefers-color-scheme) before Svelte mounts, to avoid a flash of the wrong theme.
  3. src/lib/theme/* + ThemeSelector.svelte resolve/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.