mobile-flashcard-pwa

/home/avalon/.hermes/skills/software-development/mobile-flashcard-pwa/SKILL.md · raw

Mobile Flashcard PWA

Use this when building a mobile-first study app where the card is the product.

UX rules that worked well

  1. Make the card dominate the viewport.
  2. Keep non-card chrome tiny — use a discreet top bar only.
  3. Remove redundant bottom controls if swipe + tap already cover navigation.
  4. On first use, show a small toast: "Swipe sideways to change cards. Tap the card to flip."
  5. Persist a seenGestureHint flag in localStorage so the toast is not permanent.
  6. If card metadata includes component symbols (like trigrams), show mini visual glyphs next to their names — text alone feels too abstract.
  7. For alphabet/symbol-learning apps, do not ship a small conceptual demo with only representative signs if the user wants to “learn the letters/symbols.” Build complete deck coverage first: explicit counts per script, all glyphs visible in a grid, large study cards, search, progress states, and quiz mode. Include a coverage card so gaps are obvious.

Good top bar pattern

Gesture pattern

Sequence mode pattern

Support multiple orderings with a single createStudyDeck() function.

Recommended modes: - canonical / King Wen - user-provided custom sequence (e.g. Human Design) - binary / structural - random

For a custom sequence supplied as an explicit 64-number array: 1. Store the array as a constant. 2. Build const CUSTOM_INDEX: Map<number, number> = new Map(order.map((n, i) => [n, i] as const)) 3. Sort with the map lookup instead of hardcoding many ifs. 4. Add a test that verifies: - first several values match exactly - last value matches exactly - all 64 entries are unique

This avoids mistakes when users hand you a specific sacred/study order they care about.

PWA update pattern that avoids reinstall

This was the important deploy requirement.

In the client

In public/sw.js

In Express

Serve these with no-cache headers BEFORE express.static(distDir): - /sw.js - /manifest.json

Example headers: - Cache-Control: no-cache, no-store, must-revalidate - Service-Worker-Allowed: / for sw.js

Backend-only token pattern

When the user wants an LLM/API token in the app but not exposed to the browser: 1. Load env on the server with import 'dotenv/config' 2. Keep the token in .env only 3. Never surface the raw key to the frontend 4. If useful, expose only a boolean status endpoint like /api/config-status -> { hasOpenRouter: true }

Reusing an existing OpenRouter key

If another app on the same VPS already uses OpenRouter: - Check that app’s .env or PM2 ecosystem config - Copy the key into the new app’s .env - Keep .env gitignored - Add .env.example with placeholder values only

LLM enrichment script pattern

For datasets that need generated study text (e.g. 64 cards): - Write a standalone script under scripts/ - Use OpenRouter chat completions with a strict JSON prompt - Request fields like: - overview - judgment - image - mnemonic - Save incrementally after each item so long runs can recover - Strip fenced code blocks before JSON.parse() because models may still return ```json wrappers - Merge generated JSON into the main dataset at import time rather than hardcoding everything in one file

Deployment pattern

Verification checklist

Pitfalls