From 3d1d96869654a97d205588456a0c969bca735849 Mon Sep 17 00:00:00 2001 From: Patedam Date: Mon, 2 Mar 2026 18:35:36 -0500 Subject: [PATCH] feat: document frontend technology choices for the ESP32-S3 provider, detailing the chosen stack, justifications, and build pipeline. --- Provider/tdd/frontend_technology_choices.md | 137 ++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 Provider/tdd/frontend_technology_choices.md diff --git a/Provider/tdd/frontend_technology_choices.md b/Provider/tdd/frontend_technology_choices.md new file mode 100644 index 0000000..9081aab --- /dev/null +++ b/Provider/tdd/frontend_technology_choices.md @@ -0,0 +1,137 @@ +# Frontend Technology Choices for ESP32-S3 Provider + +**Authored by Claude Opus 4.6** +**Date:** 2026-03-02 + +--- + +## 1. Goal + +Create a lightweight web frontend for the ESP32-S3 Provider that displays system information and provides a reboot control. The frontend must compile to static files small enough to live in ESP32 flash memory (~4-8kB gzipped). + +## 2. Chosen Stack + +| Technology | Version | Role | +|---|---|---| +| **Svelte** | 5 | UI framework (compiles away at build time) | +| **Vite** | Latest | Build tool and dev server | +| **TailwindCSS** | 4 | Utility-first CSS (JIT, tree-shaken at build time) | +| **vite-plugin-singlefile** | Latest | Inlines all JS+CSS into one HTML file | +| **gzip** | — | Compresses final output for ESP32 flash | + +## 3. Why Svelte Over Other Frameworks + +Svelte is a **compile-time** framework. Unlike React or Vue, it does not ship a runtime to the browser. The compiler transforms `.svelte` components into highly optimized vanilla JavaScript at build time. This means: + +- **No virtual DOM overhead** — direct DOM manipulation in the compiled output. +- **Tiny bundle size** — a simple Svelte app compiles to a few kilobytes of JS, compared to React's ~40kB runtime or Vue's ~30kB. +- **Ideal for constrained devices** — the ESP32-S3 has limited flash (typically 4-16MB shared with firmware) and serves files over a potentially slow connection. + +Other frameworks considered: + +| Framework | Runtime Size | Why Not | +|---|---|---| +| React | ~42kB min+gzip | Ships a runtime; too heavy for ESP32 | +| Vue | ~33kB min+gzip | Ships a runtime; unnecessary for a simple dashboard | +| Vanilla JS | 0kB | No tooling, harder to maintain, no component model | +| Svelte | **~2kB** min+gzip | ✅ Chosen — compiles away, smallest output | + +Vanilla JS was considered but Svelte provides a component model, reactivity, and maintainability with effectively zero runtime cost. + +## 4. Why TailwindCSS Over Vanilla CSS + +TailwindCSS v4 uses a **Just-In-Time (JIT) compiler** that scans source files and generates only the CSS classes actually used. For a simple dashboard page, this produces **under 5kB of CSS** (before gzip). + +Benefits over vanilla CSS: +- **Guaranteed small output** — unused styles are never generated (no manual purging needed). +- **Faster development** — utility classes directly in markup, no switching between files. +- **Consistent design system** — spacing, colors, typography are constrained to a scale. +- **Dark mode** — built-in `dark:` variant with zero extra effort. + +TailwindCSS is a build-time tool only. It produces a plain `.css` file — no runtime, no JavaScript. + +## 5. Why Single-File Build + +The `vite-plugin-singlefile` plugin inlines all compiled JavaScript and CSS directly into `index.html`. The ESP32 serves **one file** instead of multiple. + +Benefits for ESP32: +- **One HTTP request** — avoids multiple round-trips over a slow embedded server. +- **No filesystem overhead** — SPIFFS/LittleFS have per-file overhead; one file is optimal. +- **No filename length issues** — LittleFS has a 32-character filename limit; Vite normally generates hashed filenames like `assets/index-a1b2c3d4.js` that can exceed this. +- **Simpler backend** — the HTTP server only needs one route to serve the frontend. + +## 6. Why Not ESP32-SvelteKit + +[ESP32-SvelteKit](https://github.com/theelims/ESP32-sveltekit) is a full-featured framework that bundles SvelteKit + TailwindCSS + DaisyUI with an ESP32 backend. It was evaluated and rejected for the following reasons: + +### Incompatible Build System + +ESP32-SvelteKit requires **PlatformIO with the Arduino framework**. Our project uses **native ESP-IDF with CMake**. Adopting ESP32-SvelteKit would require rewriting all backend code (`connect.cpp`, `led_status.cpp`, `main.cpp`) to use Arduino APIs. This is not acceptable. + +### Unnecessary Complexity + +ESP32-SvelteKit includes features we do not need: +- MQTT client +- JWT-based user authentication +- WiFi provisioning UI (we already have our own wifi/ethernet logic in `connect.cpp`) +- DaisyUI theming system +- PsychicHttp web server +- ArduinoJson + +These add weight to both the firmware and the frontend bundle without benefit for our use case (a simple system info dashboard). + +### Licensing Concern + +The ESP32-SvelteKit backend code is licensed under **LGPL v3**, which has copyleft implications for linked code. Our custom setup has no such constraints. + +### What We Borrow From It + +Although we don't use the framework, we adopt the same **proven patterns**: +- Svelte + TailwindCSS + Vite as the frontend toolchain +- Gzipped static files served from ESP32 flash +- The concept of embedding frontend into firmware binary for OTA (future task) + +## 7. Build Pipeline + +``` +Source Files Build Steps Output +───────────── ─────────── ────── +src/App.svelte ─┐ +src/app.css ├─→ Vite + Svelte Compiler ─→ dist/index.html (~15-25kB) +src/lib/api.js ─┘ + TailwindCSS JIT │ + + vite-plugin-singlefile ▼ + gzip compression + │ + ▼ + dist/index.html.gz (~4-8kB) + │ + ▼ + ESP32 Flash (SPIFFS/LittleFS) + or embedded in firmware binary +``` + +## 8. Environment-Based API URL + +The frontend calls the ESP32's REST API. The base URL depends on the environment: + +| Environment | `VITE_API_BASE` | Reason | +|---|---|---| +| **Development** (PC) | `http://` | Frontend on PC, API on ESP32 over network | +| **Production** (ESP32) | *(empty string)* | Frontend and API on same device, relative URLs | + +This is handled via Vite's `.env.development` and `.env.production` files. The value is baked in at compile time — zero runtime overhead. + +## 9. OTA Considerations (Future) + +When OTA updates are implemented, the frontend will be embedded into the firmware binary as a C header array: + +1. `npm run build:esp32` → `dist/index.html.gz` (~4-8kB) +2. A script converts the gzipped file to a C `const uint8_t[]` array +3. The array is compiled into the firmware binary +4. OTA flashes one binary that includes both firmware and frontend + +This avoids needing a separate SPIFFS partition for the frontend and ensures the UI always matches the firmware version. + +## 10. Summary + +We chose **Svelte + TailwindCSS + Vite** because they are build-time tools that produce the smallest possible static output, ideal for ESP32 constraints. We build to a **single gzipped HTML file** of ~4-8kB. We did not adopt ESP32-SvelteKit because it requires PlatformIO/Arduino, which is incompatible with our native ESP-IDF project, and includes unnecessary complexity for our simple dashboard.