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/detail content dominate the viewport.
  2. Keep non-content 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.

Mobile onboarding/account flow polish

For mobile React/Vite onboarding, account setup, OAuth/device-code, or first-run flows, keep the mobile chrome and conversion steps deliberately simple: - Ensure there is exactly one visible primary menu trigger on mobile. If the brand title is needed, use a separate inert centered label rather than squeezing it into the same action row. - Hide secondary topbar actions at small widths when they crowd the brand or signup CTA; keep those actions in the drawer/sidebar instead. - Treat device codes as hero content: large centered monospace text, high-contrast block, tappable-to-copy, plus a smaller explicit copy button with temporary feedback. - Make the external authorization URL the primary CTA, and keep the final “complete setup” button disabled until the auth CTA has been opened/clicked; use precondition text like “Open authorization first.” - For iOS/mobile form inputs and autocomplete lists, set color, -webkit-text-fill-color, caret-color, suggestion background, suggestion text, and focus/hover colors explicitly.

Reference: references/mobile-onboarding-polish.md captures the Astral Hermes session-derived pattern for mobile header cleanup, device-code CTA sequencing, and birth-location autocomplete contrast.

Mobile detail modal / bottom sheet pattern

For mobile React/Vite apps with review/detail modals (draft review, creative preview, record detail, etc.), treat the modal as a bottom sheet rather than a scaled-down desktop dialog: - At max-width: 760px, align the modal overlay to the bottom, remove side padding, and give the sheet full width with rounded top corners (border-radius: 28px 28px 0 0) and max-height: ~94dvh. - Add a small non-interactive grab handle via :before; position it absolute so it does not break the modal's grid rows. - Make the modal header sticky, compact, and translucent/blurred. Clamp long titles/subtitles/status pills so they do not crowd the close button. - Collapse desktop two-column preview/copy layouts to one column. Let the main media/card preview become full width, with min-height around 180–210px and max-height around 42dvhso it feels substantial without hiding the actions. - Put destructive/approval/push actions in a sticky bottom action bar with safe-area padding, blur, and a subtle top border/shadow. Emphasize the primary action by spanning it across columns or ordering it first. - Make schedule/date inputs and action buttons at least ~42–44px tall for thumb taps. - Add a narrower breakpoint (max-width: 390px`) to collapse two-column action/copy grids further on small phones.

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