Divan — Open Day
Divan is a bilingual Persian-poetry experience built for a Persian Society Open Day stall. A visitor selects Hafez or Rumi, holds an intention, and receives a single reviewed verse with its English translation, source attribution, and optional recitation. The app works offline after its first load and is privacy-first by construction: no cookies, trackers, backend, or visitor input, so it runs reliably on a venue's unreliable network while collecting nothing about the people who use it.
01 / Overview
- 01Vite
- 02React 19
- 03strict TypeScript
- 04Hand-written service worker
- 05Web Crypto (shuffle bag)
- 06Vitest (unit / component / a11y / offline)
- 07Playwright (Chromium e2e)
- 08pnpm 10.33 · Node 22.16
Problem
A public Open Day stall needs a fast, delightful cultural experience that works on unreliable venue Wi-Fi, respects visitor privacy completely, and treats Persian poetry with cultural and scholarly integrity: no hallucinated verses, no rasterised calligraphy, no data collection.
Solution Overview
- Curated exactly 60 Hafez + 60 Rumi verses (120 total), each bound to an immutable edition with documented, verifiable source provenance
- Made the whole app offline-first with a hand-written service worker so the stall keeps working with no network
- The Web Crypto shuffle bag selects verses on-device; no server records what visitors draw
- Rendered live Nastaliq typography and per-poet art direction for cultural authenticity instead of flattened images
- Built accessibility in from the start: bilingual/RTL markup, bidirectional isolation, live regions, forced-colors and reduced-motion support
02 / Evidence
Proof & Verification
- No cookies, trackers, or backend are used. sessionStorage holds only public poem IDs
- Runs fully offline after first load via the integrity-verified service worker
- A production compiler rejects synthetic test data before deployment, so only the reviewed corpus ever ships
- Tested across unit, component, accessibility, offline, share, performance, and security suites (Vitest) plus Playwright Chromium e2e
03 / Methods
Build
Tech Stack
- Vite
- React 19
- strict TypeScript
- Hand-written service worker
- Web Crypto (shuffle bag)
- Vitest (unit / component / a11y / offline)
- Playwright (Chromium e2e)
- pnpm 10.33 · Node 22.16
- Reviewed bilingual corpus: exactly 60 Hafez + 60 Rumi = 120 verses, each bound to an immutable edition with documented source provenance
- Verse selection uses a Web Crypto shuffle bag computed on-device, without server involvement
- Offline-first: a hand-written service worker with integrity verification keeps the stall running on flaky venue networks
- Culturally distinct art direction per poet with live Nastaliq typography (real Persian script, never rasterised images)
- Bilingual markup with directional text handling, bidirectional isolation, live regions, forced-colors and reduced-motion support
Secure
- Privacy by construction: no cookies, identifiers, trackers, or visitor input; nothing is transmitted
- The client is static and has no backend, database, or analytics infrastructure
- Only public release/poem IDs persisted in sessionStorage; motion preference stored locally only
- Production compiler rejects synthetic/test data before deployment
- Source-bound attribution with rights verification (MIT for code; poetry and translations retain their own licences, e.g. CC BY-SA)
- Delivered as a static container behind an outbound tunnel, with no inbound surface