Replace the OS system font stack with IBM Plex: the variable-weight
sans (100-700, one file per subset) for UI and headings, and Plex Mono
400 for the public keys, packet hashes, and hex that font-mono renders
across the app. Latin + latin-ext subsets only (shipped locales are
en/nl); no italics; mono is never rendered bold here.
Fonts are self-hosted from @fontsource packages via the existing
build.js vendor pipeline (no CDN), copied to static/vendor/fonts/ with
a hard build failure on wrong filenames. Wiring:
- input.css: @theme --font-sans/--font-mono + @font-face rules with
unicode-ranges taken verbatim from the package CSS; Tailwind v4
derives the document default from --font-sans, re-fonting daisyUI
components with no other changes.
- spa.html: preload the latin sans variable woff2 (crossorigin, URL
identical to the @font-face src) to minimize FOUT.
- charts.js: Chart.defaults.font.family to match (Chart.js otherwise
uses Helvetica/Arial).
- error.html: name-prepend only; the page stays dependency-free.
- middleware.py: long-term immutable cache for /static/vendor/fonts/
(stable names referenced from CSS, so ?v= versioning can't apply),
with a matching test.
- app.css: slight hero-title letter-spacing tightening for Plex at
display sizes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bundle SPA JavaScript with esbuild for production builds, generating
content-hashed filenames for immutable caching. Vendor assets (Leaflet,
Chart.js, QRCode.js) get SHA256-based query params. Locale JSON files
get a combined hash version. Falls back to unbuiltsources when dist/
is absent.