6. A vanilla TypeScript PWA with a size budget and lazy chunks#
Date: 2026-10-19 (design system: 2026-10-19/20)
Status#
Accepted
Context#
The phone app opens from an icon, offline, one-handed. It also needs heavy pieces (an Excalidraw editor, Markdown rendering, Mermaid) that must not slow that first screen. The project’s goal is to stay tiny: no UI framework, icon library or web font.
Decision#
Vanilla TypeScript built by Vite into the Python package (
scrap/static). React exists only inside the lazily loaded Excalidraw chunk.The app shell has a gzipped JavaScript budget (16.5 kB, raised from the original 15 kB), enforced by
npm run sizein CI. The service worker precaches the shell only; the lazy chunks it also keeps for offline use are those inOFFLINEinweb/vite.config.ts(the composer, editor, page editor, Markdown renderer, sharing, search filters and recent searches), while Excalidraw (~405 kB gzipped), its fonts and Mermaid load only when used.The server gzips scripts, styles, pages and JSON, so the gzipped sizes are what crosses the wire.
One design system, all tokens in
tokens.css: paper and ink with a single amber accent, designed dark mode, controls under the thumb, every gesture also a button, icons always with words. Contrast is held to WCAG AA twice: a unit test over the tokens and an end-to-end sweep of real computed colours in both themes.A CSP (
font-src 'self' data:; connect-src 'self') keeps Excalidraw from reaching its CDN fallback.Pages open in a new tab as real links; swipes (row actions, paging) start away from the screen edges, which belong to the phone’s Back gesture.
Consequences#
New features usually mean a new lazy chunk, not growth of the shell.
Accessibility regressions fail CI rather than review.
No framework means some hand-written view plumbing; the trade was accepted for size and readability.