Skip to content

OPEN PANEL — Web App Spec (`SPEC_WEB.md`)

Surface spec for the WEB client. Derives from CANON.md (§3 product, §4 stack, §6 contracts, §9 terminology). Where this doc and CANON disagree, CANON wins. Status: DRAFT v0.1 — 2026-06-13. Brand nouns ALL-UPPERCASE in prose per style guide.


WEB is the primary reach surface and the free reading front door for OPEN PANEL.

  • Reach: zero-install, linkable, the canonical URL for any STANDARD ISSUE. Every share, search result, and embed resolves here first.
  • SEO discovery: the only surface crawlers see. Series, issues, and creator pages are server-rendered so the FOUNDATION catalog is fully indexable.
  • Free-first: every STANDARD ISSUE reads with no account, no paywall, no DRM (CANON §4).
  • Commerce on-ramp (not the focus): WEB hosts the VARIANT store, the licensing-portal entry, and donations — but reading is the hero. One identity, two contexts (reader vs. collector/buyer/licensee), per CANON §3.
  • Non-goal: WEB is not the heavy offline-reading surface (that is MOBILE) nor the collector’s-library / creator-tooling host (that is DESKTOP). WEB does lightweight PWA offline only (read-later, §6).

Rendering: SSR = server-rendered for SEO/first-paint; CSR = client-rendered (auth/ interactive); ISR = incrementally regenerated static. Reader engine = @openpanel/reader.

RoutePageRenderNotes
/Home / DiscoverSSR (ISR)Featured, new STANDARD ISSUES, curated rails. Indexable.
/discoverBrowse / genres / filtersSSR (ISR)Faceted catalog; filter state in URL.
/series/[slug]Series pageSSRSynopsis, issue list, credits, ratings, OG + structured data.
/series/[slug]/[issue]Issue readerSSR shell → CSR readerSSR metadata + first page for crawl/LCP; reader hydrates.
/search?q=Search resultsSSR (query)Postgres FTS (CANON §4); shareable result URLs.
/creator/[slug]Creator pageSSRBio, bibliography, links; structured Person data.
/accountAccount homeCSR (auth)Profile, reading history, entitlements, prefs.
/account/libraryMy library / read-laterCSR (auth)Progress-synced; offline-cached subset.
/storeVARIANT storeSSR (ISR)Premium VARIANT EDITIONS catalog; indexable.
/store/[product]VARIANT productSSRStripe checkout entry; OG product data.
/licenseLicensing portal entrySSRMarketing + entry to LEDGER-backed portal (handoff to backend portal).
/donateDonate (FOUNDATION)SSR → CSRStripe (separate FOUNDATION account per CANON §4).
/u/[handle]Public reader profile (optional)SSRShareable shelves; privacy-gated.
/legal/*, /aboutStaticSSGMission, governance summary, policies.
/auth/*Sign in / callbackCSRSupabase Auth; magic link + OAuth.
/api/*BFF route handlersserverThin proxy to Content API / entitlements; never expose service keys.

URL = identity: slugs are stable, human-readable, locale-prefixed for i18n (/[locale]/...).


Built on the shared @openpanel/reader TS engine (CANON §4) consuming CPF (CANON §6.1).

  • Page view: full-page reading, single/double-page spreads, fit-width / fit-height / original. Responsive image tiles served from R2/CDN.
  • Guided view: panel-by-panel pan/zoom along the authored CPF reading path (panel regions + order). Smooth animated transitions; tap/click or arrow to advance panel. Falls back to page view if a CPF has no guided regions.
  • Keyboard nav: ←/→ page or panel, Space/Shift+Space advance/back, F fullscreen, G toggle guided/page, +/- zoom, Home/End first/last, ? shortcut help. Full focus management and visible focus ring (WCAG, §7).
  • Fullscreen: Fullscreen API; chrome auto-hides, reveals on pointer move / key.
  • Progress sync: current page/panel persisted to the Content API (CANON §6.2) for signed-in readers, debounced; resumable across surfaces. Anonymous readers get localStorage progress that can be claimed on sign-in.
  • Prefetch: next N pages/tiles prefetched (idle + network-aware); guided view prefetches the next panel’s tile. Uses <link rel=prefetch> + engine-driven cache warming.
  • Low-bandwidth mode: user toggle + auto via navigator.connection (Save-Data / slow effective type) → lower-resolution tiles, no animated guided transitions, lazy prefetch off.
  • Reader prefs (persisted): background (paper/ink/sepia), reading direction (LTR/RTL per CPF locale), dyslexia-friendly UI font, reduced-motion (honors prefers-reduced-motion).
  • Accessibility in-reader: per-panel alt-text from CPF surfaced to screen readers in guided order; “read alt-text” mode (CANON §3).

  • Installable: Web App Manifest (name, icons, theme, display: standalone, shortcuts to Home + My Library). Install prompt surfaced contextually, not nagging.
  • Service worker strategy:
    • App shell + static assets → stale-while-revalidate.
    • Catalog/series JSON → network-first (fallback to cache offline).
    • Comic page tiles → cache-first with an explicit “save for offline / read-later” action that bulk-caches a STANDARD ISSUE’s tiles + its CPF into a named cache.
    • Auth/entitlement calls → network-only (never cached).
  • Read-later quota: capped cache with LRU eviction + user-visible storage usage in /account/library; eviction never silently drops an explicitly saved issue without notice.
  • Scope boundary: WEB offline is for casual read-later; the durable offline-library experience is MOBILE/DESKTOP (CANON §3). Premium VARIANT content is not cached offline on WEB (entitlement-gated, §5).

Per CANON §6.3 (Identity & Entitlements) and §4 (Supabase Auth, one identity).

  • Free is always granted. STANDARD ISSUES require no auth and no entitlement check. Auth only unlocks personalization (progress sync, library, prefs) and premium VARIANT.
  • Auth: Supabase Auth — magic-link primary + OAuth providers. One identity spans reader/buyer/licensee contexts; context is a UI mode, not a separate login.
  • Premium gating: VARIANT EDITIONS are gated by entitlement checks, not hard DRM (CANON §4 DRM stance). The reader requests an entitlement token from the Content API; gated tiles served via short-lived signed CDN URLs. No client-side secret ever.
  • Minors / COPPA (CANON §7.7): age-gated account creation, restricted data collection and no behavioral profiling for minors; default content rating filter on.
  • Server enforcement: entitlements enforced server-side (RLS + Rust services); the client UI gating is convenience only, never the security boundary.

  • SSR catalog (CANON §4) for home, discover, series, issue shell, creator, store.
  • Open Graph / Twitter cards: per series/issue/creator/product — cover art, title, synopsis, creator credits.
  • Structured data (JSON-LD): ComicSeries, ComicIssue, Person (creators), Product + Offer (VARIANT store), BreadcrumbList, Organization (FOUNDATION).
  • Sitemaps: auto-generated, segmented (series, issues, creators, store), lastmod from catalog; submitted via robots.txt. Canonical URLs + hreflang for locales.
  • Crawlability: issue reader ships SSR metadata + a crawlable first-page/cover so the free catalog is fully indexed even though the reader itself hydrates client-side.

Performance budgets (Core Web Vitals targets):

  • LCP ≤ 2.5s (p75, mobile 4G); INP ≤ 200ms; CLS ≤ 0.1.
  • Initial JS (reader route) ≤ ~180KB gzip; route-level code splitting; reader engine lazy-loaded.
  • Images: responsive srcset + tiled comic pages from R2/CDN; AVIF/WebP with fallback; explicit dimensions to prevent CLS; priority hint on the cover/first page.
  • Edge caching for SSR/ISR catalog; CDN for all assets (CANON §4).

Accessibility — WCAG 2.2 AA:

  • Full keyboard operability (§3); visible focus; logical focus order; skip links.
  • Reader exposes alt-text per panel (CPF) to assistive tech; landmark + ARIA roles.
  • Color contrast AA on both ink and paper themes; respects prefers-reduced-motion and prefers-color-scheme; dyslexia-friendly font option; targets ≥ 24×24px.

i18n:

  • Locale-prefixed routes; UI strings externalized; RTL layout support (drives reader direction with CPF locale); localized metadata + hreflang; per-CPF locale content.

  • Sourced from brand/BRAND_IDENTITY.md (CANON §1): panel-grid + gutter motif, ink-black / paper-white base, FOUNDATION = signal/ink blue, VARIANT = collectible gold/foil, newsprint texture; monospace + humanist sans system, display face for logos.
  • Reader chrome is a distinct, minimal component set (top bar, page/panel scrubber, settings sheet, fullscreen toggle) that recedes during reading. Catalog/store/account use the standard system components.
  • Tailwind design tokens mirror BRAND_IDENTITY; two sub-brand accent themes (FOUNDATION / VARIANT) toggled by context. No bespoke per-page CSS outside the token system.

Analytics events (privacy-respecting, CANON §6.7 / §4 — no third-party ad trackers, COPPA-aware):

  • page_view, series_view, issue_open, reader_start, page_turn, panel_advance, guided_toggle, reading_complete, progress_sync.
  • save_for_offline, pwa_install, low_bandwidth_toggle.
  • search_query (terms hashed/aggregated), search_result_click.
  • auth_sign_in, entitlement_check, variant_view, checkout_start, donate_start.
  • Minors: engagement events stripped of identifiers; no behavioral profiling.

Security:

  • CSP: strict default-src 'self'; explicit allowlist for R2/CDN, Stripe, Supabase; no inline scripts (nonce/hash); frame-ancestors 'none'; HSTS; Referrer-Policy.
  • RLS reliance: all reader/entitlement/account data behind Supabase RLS + server enforcement; the WEB BFF (/api/*) holds no service-role key client-side (CANON §4 / RINA rule: non-NEXT_PUBLIC_ vars are server-only).
  • Signed, short-lived CDN URLs for any gated VARIANT tile; Stripe via hosted/Elements only, no raw card data touches our origin; separate Stripe accounts for VARIANT vs. FOUNDATION donations (CANON §4).

10. Acceptance criteria — MVP definition of done

Section titled “10. Acceptance criteria — MVP definition of done”
  1. Anonymous user can open /series/[slug]/[issue] and read a full STANDARD ISSUE in both page and guided view, with keyboard + touch nav and fullscreen — no account.
  2. Home, discover, series, creator, and issue pages are SSR and indexable, with valid OG tags, JSON-LD (ComicSeries/ComicIssue/Person), and a generated sitemap.
  3. Search returns relevant series/issues via Postgres FTS at a shareable URL.
  4. Signed-in reader gets progress sync that resumes across a reload and is claimable from prior anonymous reading.
  5. PWA is installable; a saved STANDARD ISSUE is readable offline (read-later), with visible storage usage and no silent eviction of saved issues.
  6. A premium VARIANT EDITION is entitlement-gated: blocked without entitlement, readable with one; enforcement is server-side, no client secret.
  7. /store, /donate, /license entries function; VARIANT checkout and FOUNDATION donation route to the correct separate Stripe accounts.
  8. CWV targets met at p75 on a mid-tier mobile device (LCP ≤2.5s, INP ≤200ms, CLS ≤0.1).
  9. WCAG 2.2 AA passes automated + manual keyboard/screen-reader audit on reader + catalog; reduced-motion and dyslexia-friendly options work.
  10. CSP enforced with no violations in normal flows; no service-role key reachable client-side; gated tiles only via short-lived signed URLs.
  11. At least one non-English locale renders end-to-end (UI + a localized CPF), with hreflang and RTL verified.