Skip to content

About

A beautiful pastel score pad for the board game Harmonies — landscapes, animal cards, saved games (React + Vite, Capacitor-ready)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

Harmonies Scorepad

A quiet score pad for the board game Harmonies (Johan Benvenuto, Libellud, 2024). Count five landscapes, the animal cards and nature's spirit, save the sheet on your device, and keep a small record of every game at the table.

A mobile-first web app, installable as a PWA and ready to ship to Android and iOS through Capacitor. No account, no network, no backend: everything stays on the device.

Screens

Route Screen State
/ Score pad — game title, date, board side, one column per player with live totals, and a sticky rail of details and actions from 1280px Complete
/records Saved games, search, sort, density, JSON/CSV export, JSON import, delete with undo Complete
/games/:id Saved game detail: per-player landscape and animal breakdown, notes, winner, duplicate/export/delete Complete
/stats Games played, seats, best score, average winning score, per-player table and bars Complete
/help How to score, with the printed points tables and a section index Complete

Every route is wrapped by the shell: sticky top bar, page title, and a menu panel (score pad / records / stats / how to score, language, theme, data). Below 900px the navigation lives only in that drawer; there is no sideways-scrolling nav row.

Stack

  • React 18 + TypeScript (strict) + Vite 5
  • Tailwind CSS 3.4, tokens as CSS variables in apps/web/src/index.css
  • zustand 5 with persist (localStorage), versioned and migrated
  • i18next + react-i18next, three bundled locales: en, zh-HK (default), zh-CN
  • motion (motion/react) microinteractions, reduced-motion aware
  • Vitest + Testing Library for the scoring engine, store, derivations and i18n parity
  • Capacitor 6 config only — no native folders are committed

The entry chunk carries the score pad; /records, /games/:id, /stats and /help are loaded on first visit behind a component-level skeleton. React, the router, i18next and the animation runtime sit in named vendor chunks, so a copy or artwork change never invalidates them.

Commands

npm install            # from the repository root
npm run dev            # Vite dev server, http://localhost:5173
npm run build          # tsc -b && vite build
npm run preview        # serve dist on http://localhost:4173
npm test               # vitest run (86 tests)
npm run typecheck      # tsc --noEmit -p apps/web/tsconfig.json
npm run lint           # eslint .

Individual workspace commands work too:

npm run dev -w harmonies-score-web
npm test -w harmonies-score-web

Scoring rules

The app implements the printed sheet exactly. apps/web/src/lib/scoring.ts is the single source of truth; every screen reads its totals from there.

Landscape Points
Trees height 1 = 1, height 2 = 3, height 3 = 7
Mountains height 1 = 1, height 2 = 3, height 3 = 7 (a lone mountain scores 0)
Fields 5 per group of 2+ connected yellow tokens
Buildings 5 each, only when 3+ different token colours touch it
Water, side A (River) longest river only: 2 = 2, 3 = 5, 4 = 8, 5 = 11, 6 = 15, then +4 per extra token; 0–1 = 0
Water, side B (Islands) 5 per island
Animals the points printed on each card's lowest empty cube space
Nature's spirit optional variant, defaults to 0

Total = landscapes + animal cards + nature's spirit. Highest total wins; a tie is broken by the number of animal cubes placed.

Worked example on board side A:

Sheet Trees Mountains Fields Buildings River Animals Spirit Total
3×h2 + 1×h3 trees, 2×h1 mountains, 3 fields, 2 buildings, river 7, five cards (10/7/5/3/2), spirit 4 16 2 15 10 19 27 4 93
2×h1 + 2×h2 trees, 1×h3 mountain, 2 fields, 1 building, river 5, three cards (8/4/2) 8 7 10 5 11 14 0 55

Data & files

Everything stays on the device. The store persists to localStorage["harmonies.scorepad.v1"] as { draft, saved } — a refresh never drops a saved game. Records can be exported as JSON (exportJson) or CSV (toCsv, one row per player, UTF-8 BOM for Excel) and re-imported (importJson, shape-validated with typed ImportError codes). Importing merges: duplicate ids are skipped, saved games are never replaced, and the toast states how many arrived.

Dates are stored as local wall-clock strings (2026-10-18T19:30) taken straight from <input type="datetime-local">. No timezone conversion is applied anywhere.

Design

See docs/DESIGN.md for the palette (with measured contrast), type scale, spacing, primitives, motion and art direction. The short version: warm paper, watercolour art, delicate ink rules, hexagonal landscape tokens, numbers in Fraunces, and one right edge for every column of figures.

Verification

npm run build && npm run preview      # measure the built output, not the dev server
python3 "$HOME/.hermes/skills/kin/02-Creative/kin-ui/scripts/i18n_keys.py" apps/web

The translation audit must report MISSING=0, DRIFT=0 and UNUSED=0 (298 keys in each locale). Layout and colour are checked on the built page at 360, 390, 768, 1024 and 1440px in both themes: no horizontal scroll, every text/background pair at or above 4.5:1 (3:1 for large text and control borders), no broken images, webfonts loaded. See AGENTS.md for the full pre-flight list.

Mobile

Capacitor reads apps/web/capacitor.config.ts (com.ashluc.harmonies, Harmonies Scorepad, webDir: dist). The native folders are not committed; create them once on the machine that will build the app.

# One-time: add a platform shell (creates apps/web/android or apps/web/ios)
npm run cap:add:android
npm run cap:add:ios          # macOS + Xcode only

# Every time the web app changes: build, then copy dist + plugins into the shells
npm run build:mobile

# Open the native project
npm --workspace harmonies-score-web run android
npm --workspace harmonies-score-web run ios

build:mobile runs the production build and then npx cap sync from apps/web. The splash and status-bar defaults in capacitor.config.ts use the paper colour, and the manifest and icon set are what the PWA install uses on the web.

Art credits

Painted watercolour assets live in apps/web/public/art/ (WebP, 720×720 tokens, 2048×1152 / 1024×1536 heroes). The app falls back to its own hand-drawn SVG icons when a file is absent, so the interface never shows a broken image. Icons are hand-authored in src/components/icons.tsx; no icon library is bundled. The app icons in apps/web/public/icons/ are rendered from the same leaf mark as the favicon.

Harmonies is designed by Johan Benvenuto and published by Libellud. This is an unofficial score pad made for a home table.

About

A beautiful pastel score pad for the board game Harmonies — landscapes, animal cards, saved games (React + Vite, Capacitor-ready)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages