dex/README.md
chris 344e641475 Cosmetic forms: compact Appearance picker
Pull sprite-only variants into the snapshot — Unown letters, Vivillon
patterns, Furfrou trims, seasonal Deerling/Sawsbuck, Flabébé-line colours,
Alcremie decorations, Pikachu caps (278 across 50 species). They share the
base Pokémon's typing/stats/abilities/learnset, so they carry only
{ slug, id, name, sprite } and never trigger a /pokemon fetch.

build-snapshot.mjs: cosmetic varieties (previously dropped) and the
/pokemon-form entries for species with >1 form now land in
species.cosmeticForms.

PokemonDetail: a single <select> "Appearance" dropdown, separate from the
mechanical form pills, that swaps only the hero artwork + label. Picking a
cosmetic look while a mechanical form is active reverts to base first.
Card "+N forms" badge counts cosmetic variants too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Ve7HLspzeG2xDPtJQ8vmu
2026-08-28 11:59:20 -04:00

115 lines
6.1 KiB
Markdown

# Pokédex PWA
A responsive, offline-capable Pokédex reference built on
[PokéAPI](https://pokeapi.co/). Vanilla JS + Vite + Workbox — no UI
framework.
## Two headline features
| Feature | How it works |
| --- | --- |
| **Game series selection** | Pick a *game* (PokéAPI version group, e.g. Scarlet & Violet). If it has more than one regional dex (Paldea / Kitakami / Blueberry) a sub-dex switcher appears. The choice drives which species show, their regional numbering, and which version's flavor text the detail page uses. Stored in `localStorage`. |
| **Unified Pokémon selection** | One tracking record per Pokémon, keyed by **National Dex id**. Mark Pikachu once and every game's view reflects it — each dex computes its own "Seen / Caught" totals by intersecting its species list with that single map. The active Pokémon is also a single deep-linkable route (`#/pokemon/25`). |
## Data & storage split
- **Preferences + tracking state** → `localStorage` (`pdx.settings`, `pdx.selection`). Tiny, synchronous, restored on reload.
- **Build-time snapshot** (`src/data/snapshot.json`, ~150 KB) → generated by `npm run snapshot` from PokéAPI: every species (id, name, generation, types), all regional Pokédex lists, and all version groups. Precached by the service worker, so the list / search / game switching work on the first offline launch.
- **Detail data** (stats, abilities, flavor text, evolution) → fetched lazily from `pokeapi.co` per Pokémon, then cached by the service worker (stale-while-revalidate, 30-day TTL).
- **Sprites** → cache-first with a capped LRU.
Export / import of `settings` + `selection` as JSON lives in **Settings**.
## Scripts
```bash
npm install
npm run snapshot # fetch data from PokéAPI -> src/data/snapshot.json (run once; refreshes if >30 days old)
npm run dev # vite dev server
npm run build # runs snapshot (prebuild) then vite build
npm run preview # serve the production build (needed to exercise the service worker)
```
Deploying under a sub-path (e.g. GitHub Pages project site): `BASE_PATH=/repo-name/ npm run build`.
## Docker
Multi-stage build (Node → nginx) that serves the static `dist/`:
```bash
docker compose up --build # → http://localhost:8080
# or without compose:
docker build -t pokedex-pwa .
docker run --rm -p 8080:80 pokedex-pwa
```
The build stage runs `npm run build`, whose `prebuild` step fetches the
PokéAPI snapshot — so `docker build` needs network access to `pokeapi.co`
unless a fresh `src/data/snapshot.json` is already present (it gets copied in
and reused). Serving under a sub-path: `docker build --build-arg BASE_PATH=/dex/ .`.
`nginx.conf` sets the SPA fallback and long-cache headers for `/assets/*`
while keeping `index.html` / `sw.js` uncached.
## Project layout
```
scripts/build-snapshot.mjs PokéAPI -> snapshot.json
src/
main.js boot: theme, nav, router, SW registration
router.js hash router (#/, #/pokemon/:id, #/games, #/search, #/settings)
sw.js Workbox service worker (injectManifest)
store/ createStore + settings + selection (localStorage)
data/ snapshot loader, pokedex resolver, lazy API client
components/ Card, Sprite, TypeChip, StatBar, ProgressHeader, Nav
views/ DexGrid, PokemonDetail, GamePicker, SearchView, SettingsView
styles/ tokens.css (palette, type colors, light/dark), layout.css
```
## Status
Working app with a type-themed UI:
- **Dex grid** — type-tinted cards (official artwork, spotlight, ghost number),
per-dex progress, name/number filter, caught/favorite filters.
- **Forms** — Megas, Gigantamax, regional forms and alternate formes in the
snapshot; a pill switcher on the detail page rebuilds types / stats /
matchups / abilities / learnset / artwork for the chosen form. Purely
cosmetic variants (Unown's 27 letters, Vivillon's 19 patterns, Furfrou
trims, seasonal Deerling/Sawsbuck, Flabébé line colours, Alcremie's 62
decorations, Pikachu caps — 278 across 50 species) get a separate compact
"Appearance" dropdown that only swaps the sprite. Cards show a "+N forms"
badge.
- **Detail** — type-gradient hero + tabbed sheet (About / Stats / Evolution /
Moves / Locations); coloured animated stat bars; defensive type matchups;
evolution chain re-parented to the selected game's generation; learnset with
per-move power/type/accuracy/PP/effect (era-accurate via `past_values`,
incl. pre-Gen-4 physical/special-by-type); wild encounter locations.
- **Game-aware** — abilities gated to Gen 3+ (hidden to Gen 5+); type chart
applies Gen 1 / pre-Gen 6 rules.
- **Games** — overlay picker with stylised version-colour cover tiles,
sub-dex switch, and an "All games" (National, no gen limits) option.
- **Search** — tabbed lookup: Pokémon, Moves and Items, all browsable
offline from the snapshot. Moves filter by type / damage class / "TMs in
this game" and sort by power / accuracy / recency; Items filter by
category. Each has its own detail page. Query, scroll, tab and filters
persist.
- **Team** — a lineup of up to 6 with a Coverage table (weaknesses, STAB
gaps) and a Compare table; toggleable in the nav from Settings.
- **Settings** — theme (System / Light / Dark / Black / Sepia) + accent
colour, sprite style, JSON export/import.
- **PWA** — Workbox SW: precache shell + snapshot, SWR for API JSON,
cache-first LRU for sprites, in-app update toast.
Not yet done: version-exclusive badges, type/generation filters on the grid,
an interactive region map (PokéAPI has no map imagery or coordinates),
skeleton loaders, install-prompt handling, PNG/maskable raster icons
(currently SVG only), and a Lighthouse PWA pass.
## Notes
- `npm audit` reports the known esbuild dev-server advisory via Vite 5. It
affects the local dev server only, not the production build. Vite 8 (which
fixes it) requires a newer Node than this environment has.
- Data and images © Nintendo / Game Freak / The Pokémon Company, served via
PokéAPI. This project is a non-commercial reference tool.