What
Redesign or improve the embedded Web UI — the browser interface served directly from the Pico W.
Why
The current Web UI is functional and was given a design pass in v2.2.0-beta (measured light/dark palette, --ok/--warn/--track tokens, theme-aware graph and login). There is still room in layout, mobile ergonomics, forms and error states.
⚠️ Resource constraints (corrected — read carefully)
Measured on main @ 217e28a (v2.2.14-beta, release build, 2026-08-19) |
Bytes |
| Flash used |
1,006,076 of 1,044,480 (96.3%) |
| Flash headroom |
38,404 |
Numbers here age with every release — an earlier revision said 50,268 B; h5g (v2.2.11), HA Discovery + /metrics (v2.2.13) and syslog (v2.2.14) consumed the difference. Re-measure before budgeting: arm-none-eabi-size -A on the release ELF, formula in docs/ANALISE_FLASH_RAM.md (the PlatformIO % lies). The practical budget is unchanged: a few KB of gzipped growth is affordable, a framework is not.
- No frameworks. No React, Vue, Tailwind, or npm. Static HTML/CSS/JS only.
- RP2040: 264 KB SRAM, dual-core at 133 MHz. The web server shares the chip with sensor polling, display rendering, WiFi and telemetry. Heavy JS or large DOM trees will OOM the device.
- Browser target: modern Chrome, Firefox, Safari, Edge. Mobile-first (most users arrive from a phone).
- i18n ceiling (the hidden cost). Every new visible string ships in BOTH
.lng packs, and language_es-ES.lng is at 97% of its 32,768 B ceiling (723 B free). Overflow is invisible at runtime — python3 tools/check_lang_packs.py is the only guard. Prefer reusing existing visible labels (e.g. aria-labelledby) over minting new strings; text-heavy improvements (error messages) need a pack diet first.
How the pipeline actually works (corrected)
WebUI.h is the source (currently 452,625 B of raw-string HTML/CSS/JS). It is not shipped as-is:
tools/build_webui_gz.py runs as a pre-build hook — it minifies each page, gzips it, and writes src/WebUI_GZ.h.
- The minifier is a stateful scanner, not a regex, because the regex predecessor silently ate JS inside string literals. It is guarded by
tools/test_webui_minify.py — run it after any edit; a page that stops being literal-equivalent fails the gate.
custom_fs_pages can move a page out of flash and into LittleFS, but build_webui_gz.py refuses that option for shipping environments — it is a bench-only diet lever and is not a way to buy space for a UI change.
So the loop is: edit WebUI.h → python3 tools/test_webui_minify.py → pio run -e pico_w_release → read the gzipped size delta out of the build.
What you can improve — pick ONE
- Dashboard cards — temperature/humidity as cards instead of raw tables
- Mobile-first responsive — touch-friendly controls, no horizontal scroll at 375px
- Settings form UX — grouping, validation, save/cancel patterns
- Loading states — CSS-only spinner, skeleton screens
- Accessibility — ARIA labels, keyboard nav, focus management
- Error states — friendly messages, retry buttons, offline indicators
- Login page — modern and branded, under 1 KB of added CSS
Already done, do not pick: dark mode. It shipped in v2.2.0-beta as a full light/dark palette chosen by measured contrast, not a toggle bolted on top.
Acceptance
References
WebUI.h — UI source
tools/build_webui_gz.py — minify + gzip pre-build hook
tools/test_webui_minify.py — equivalence gate
src/WebManager_*.cpp — server-side handlers
What
Redesign or improve the embedded Web UI — the browser interface served directly from the Pico W.
Why
The current Web UI is functional and was given a design pass in v2.2.0-beta (measured light/dark palette,
--ok/--warn/--tracktokens, theme-aware graph and login). There is still room in layout, mobile ergonomics, forms and error states.main@ 217e28a (v2.2.14-beta, release build, 2026-08-19)Numbers here age with every release — an earlier revision said 50,268 B; h5g (v2.2.11), HA Discovery +
/metrics(v2.2.13) and syslog (v2.2.14) consumed the difference. Re-measure before budgeting:arm-none-eabi-size -Aon the release ELF, formula indocs/ANALISE_FLASH_RAM.md(the PlatformIO % lies). The practical budget is unchanged: a few KB of gzipped growth is affordable, a framework is not..lngpacks, andlanguage_es-ES.lngis at 97% of its 32,768 B ceiling (723 B free). Overflow is invisible at runtime —python3 tools/check_lang_packs.pyis the only guard. Prefer reusing existing visible labels (e.g.aria-labelledby) over minting new strings; text-heavy improvements (error messages) need a pack diet first.How the pipeline actually works (corrected)
WebUI.his the source (currently 452,625 B of raw-string HTML/CSS/JS). It is not shipped as-is:tools/build_webui_gz.pyruns as a pre-build hook — it minifies each page, gzips it, and writessrc/WebUI_GZ.h.tools/test_webui_minify.py— run it after any edit; a page that stops being literal-equivalent fails the gate.custom_fs_pagescan move a page out of flash and into LittleFS, butbuild_webui_gz.pyrefuses that option for shipping environments — it is a bench-only diet lever and is not a way to buy space for a UI change.So the loop is: edit
WebUI.h→python3 tools/test_webui_minify.py→pio run -e pico_w_release→ read the gzipped size delta out of the build.What you can improve — pick ONE
Already done, do not pick: dark mode. It shipped in v2.2.0-beta as a full light/dark palette chosen by measured contrast, not a toggle bolted on top.
Acceptance
WebUI.hpython3 tools/test_webui_minify.pypassespio run -e pico_w_release(zero warnings)python3 tools/check_lang_packs.pystill passes (both packs under the 32 KB ceiling)References
WebUI.h— UI sourcetools/build_webui_gz.py— minify + gzip pre-build hooktools/test_webui_minify.py— equivalence gatesrc/WebManager_*.cpp— server-side handlers