Mobile fixes (all in the embedded SPA): - Move the mobile media queries to the bottom of the stylesheet — the cinematheque design layer was appended after them, so its base rules (header padding, video heights) silently overrode the phone layout by source order. A comment now pins the ordering invariant. - The design layer's uiDrop entry animation (fill-mode:both) permanently overrode the sidebar's translateX(-100%), leaving the drawer stuck open over the content on phones; the mobile block now disables it. - Lower the single-column breakpoint 380px -> 350px: it was catching 360/375px phones and blowing every card up to full width. - viewport-fit=cover so the env(safe-area-inset-*) paddings actually resolve on iPhones; notch-safe padding on header/content/modals. - 16px inputs on small screens (kills iOS Safari's focus auto-zoom), same for the login page's password field. - Mobile rules for the design-layer components that had none: full-height command-palette sheet, clamped stats numerals, coarse-pointer touch targets for the custom player (taller scrub track, always-visible thumb, no volume slider). - CSP font-src now allows data: — the embedded base64 woff2 fonts were being blocked and silently fell back to system faces. PWA: - manifest.webmanifest, minimal service worker, and a new Catacomb arch icon set (SVG source + rendered PNGs incl. maskable + apple-touch), all include_str!/include_bytes!-embedded like the HTML. - sw.js only intercepts GET navigations to "/" (network-first, cached offline fallback) and the static assets; /api, /ws, /files, /music-files, /feed are never touched, and it's served no-store so binary upgrades keep propagating. - Routes + auth_middleware allowlist so the browser can fetch the statics pre-login; theme-color meta tracks the active theme's panel. - tests/api.rs covers the new endpoints incl. the ungated-when-password invariant. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 KiB
4 KiB
Mobile scaling fixes + PWA support — design
Date: 2026-07-05. Scope: src/web_ui/ + src/web.rs + tests/api.rs.
Problem
- Mobile scaling regressions. The "private cinematheque" design layer was
appended after the
@media(max-width:640px)/@media(max-width:380px)blocks insrc/web_ui/index.html. Media queries add no specificity, so the design layer's base rules (header padding,.vid-wrap videomax-height, …) override the mobile rules by source order. The design layer also introduced whole components with no mobile rules at all: the custom player (.pl-*), command palette (.cp-*), stats dashboard (.sx-*) and maintenance console (.mx-*). - The viewport meta lacks
viewport-fit=cover, so everyenv(safe-area-inset-*)the CSS relies on evaluates to 0 on iPhones. - Text inputs are 13px, which triggers iOS Safari's automatic focus-zoom — the classic "the page scales itself when I tap search" bug.
- No PWA support: no manifest, no service worker, no installability, and the
bundled
icon.pngis a YouTube-logo lookalike unsuitable for an installed app icon.
Approach chosen
Mobile fixes (index.html only, no JS-visible id/class changes):
- Add
viewport-fit=coverto the viewport meta. - Move both mobile media-query blocks to the end of the stylesheet, after
the design layer, with a comment stating the ordering invariant. This is the
structural fix; alternatives (raising specificity,
!important) were rejected as fragile. - Inside the ≤640px block: bump text inputs/selects to 16px (kills iOS
focus-zoom), and add rules for the new components — full-bleed command
palette with
dvhsizing, clamped stats-hero numerals, safe-area padding. - New
@media(pointer:coarse)block for touch ergonomics independent of width: taller scrub track, always-visible seek thumb, ≥40px player buttons, hide the hover-oriented volume slider (phones use hardware volume).
PWA (additive, all static assets embedded like the existing HTML):
src/web_ui/manifest.webmanifest— name/short_name Catacomb,display: standalone,start_url: /,id: /, theme/background#1a1a2e, icons 192/512 + maskable 512.- New Catacomb-branded icon:
src/web_ui/icon.svg(crimson catacomb arch on the app's dark palette) rendered toicon-192.png,icon-512.png,icon-maskable-512.png,apple-touch-icon.png(180px, opaque). SVG source checked in; PNGs regenerated viarsvg-convert. src/web_ui/sw.js— deliberately minimal service worker. Intercepts only GET navigations to/(network-first, falling back to a cached copy when offline) and the static icon/manifest paths (cache-first). It never touches/api/*,/ws/*,/files/*,/music-files/*, or/feed*— no interference with auth, streaming ranges, or WebSockets. Servedno-storeso upgrades propagate on next load, matching the existing "no stale UI" policy.src/web.rs—include_str!/include_bytes!consts + routes for/manifest.webmanifest,/sw.js,/icons/*,/apple-touch-icon.png; these paths are allowlisted (GET, static, nothing sensitive) inauth_middlewareso the browser can fetch them pre-login.index.htmlhead: manifest link,theme-colormeta (kept in sync with the active theme's--panelbyapplyTheme), apple-touch-icon link, iOS standalone metas; SW registration guarded by'serviceWorker' in navigator.
Testing
tests/api.rs: new test asserting/manifest.webmanifest,/sw.js, and an icon route return 200 with sane content types, and that they are reachable without auth when a password is set (follows the existing curl harness).node --checkon the extracted inline script and onsw.js.- Headless Chromium screenshots at phone viewports (375×812, 360×740) before
and after, against a real
--webinstance.
Out of scope
Offline caching of library data/media, push notifications, background sync,
replacing icon.png used by the desktop .desktop entry (flagged for a
follow-up since it's a YouTube trademark lookalike).