From f4e41d45ca5bb8271d53b682132b29d93b403f6c Mon Sep 17 00:00:00 2001 From: John Rees Date: Fri, 10 Jul 2026 18:04:06 +0100 Subject: [PATCH] docs: move menus.js and specialty-helper reference out of CLAUDE.md The menus.js API reference and the bloom/cards/gameAI/words helper docs loaded into every session regardless of whether the current prototype touched them. Moved that content into on-demand skills (.claude/skills/littlejs-menu-ui-helpers, .claude/skills/littlejs-specialty-game-helpers) so it loads only when relevant, cutting the always-resident file from ~34.9k to ~16.3k chars. The skills are namespaced with a littlejs- prefix and tracked in the repo (rest of .claude/ stays gitignored/local) so they ship with the project instead of depending on each user's local skill setup. --- .../skills/littlejs-menu-ui-helpers/SKILL.md | 37 +++++++++++++++++++ .../littlejs-specialty-game-helpers/SKILL.md | 11 ++++++ .gitignore | 5 ++- CLAUDE.md | 36 ------------------ 4 files changed, 51 insertions(+), 38 deletions(-) create mode 100644 .claude/skills/littlejs-menu-ui-helpers/SKILL.md create mode 100644 .claude/skills/littlejs-specialty-game-helpers/SKILL.md diff --git a/.claude/skills/littlejs-menu-ui-helpers/SKILL.md b/.claude/skills/littlejs-menu-ui-helpers/SKILL.md new file mode 100644 index 0000000..59931c8 --- /dev/null +++ b/.claude/skills/littlejs-menu-ui-helpers/SKILL.md @@ -0,0 +1,37 @@ +--- +name: littlejs-menu-ui-helpers +description: API reference for building title screens, pause menus, options menus, medal grids, dialogs, and HUD toolbars with templates/menus.js in this LittleJS Arcade project. Use when creating or editing any menu/UI screen for a game in games/ — title, pause, options, confirm/alert dialogs, medals, best-score display, toasts, or toolbars. +--- + +# Menu UI (when using templates/menus.js) + +- Start from templates/menuGame.html — wires up title, options (with separators + persisted slider/checkbox/color/input), medals (clickable grid + MenuMedal toasts), about (wrapping text), pause (with initialItemId), confirm/alert dialogs, HUD toolbar, and global navigation/activate sounds. +- API: createMenu({id, title, subtitle, items, dismissable, initialItemId, onShow, onHide, onStart}) and createToolbar({id, anchor, direction, items}). Item types: label, text (wrapping multi-line paragraph), separator, button, toggle, slider, checkbox, color (HTML color picker, fires onChange with hex), input (text field; arrow/Enter/Space pass through while focused), grid (per-cell onClick makes cells focusable buttons), custom (set focusable:true to opt into nav). Each menu returns a handle with show/hide/toggle/getItem/destroy. Items expose setLabel/setValue/getValue/setDisabled/setVisible. Grids also expose setCell(index, props). +- Per-item flags: `onUpdate(el)` — fires every frame while the parent menu is visible (live counters, animated text/custom DOM); `persist:'storeKey'` — slider/toggle/checkbox/color/input only, auto-loads from localStorage on init and auto-saves on change, with onChange firing once at init via a microtask so consumer effects apply the persisted value; `hideOnTouch:true` — toolbar items only, auto-hide on touch devices (fullscreen/music style buttons). +- onHide receives a reason: `'push'` when pushMenu hides this menu to surface a child, `'dismiss'` when Esc / B / backdrop / explicit hide closed the menu. Branch on it to avoid clobbering parent state when a sub-menu opens (e.g. `if (reason === 'dismiss') resetTitleFlag();`). +- createMenu's `onStart` runs when gamepad Start is pressed while the menu is on top — use it to launch the primary action (e.g. PLAY) directly without forcing the player to navigate to it first. +- Title-screen reveal: attachClickToReveal(menuId, canReveal?) installs a document-level click/Space/Enter/A/Start listener that shows the named menu the first time the user interacts with the canvas. Optional canReveal predicate (e.g. `() => !isPlaying`) gates the reveal so the listener can stay attached for the whole game. Pair with the menu's onShow hook to start title music. Returns a teardown fn. +- Title backdrop: don't leave the canvas black behind the title menu — it reads as a broken game. Build the world (board, rack, scrolling parallax, attract-mode animation) in gameInit and render it every frame from gameRender regardless of any `isPlaying` flag; only gate input on `isPlaying` so clicks fall through to the menu. menus.js's pause hook (`setMenuVisibilityCallback(v => paused = v)`) stops `gameUpdate` while the title menu is up, so the backdrop sits frozen instead of simulating, but it still renders. Reset the world on QUIT (call your `newGame()` helper from the quit confirm handler) so the title shows a clean state rather than a half-finished one. +- Title menu surfaces immediately — default. Prototypes should pass `createTitleMenu({revealOnClick:false})` and call `showMenu('title')` once at the end of `gameInit` so the menu appears on page load without a click. The click-to-reveal pattern (`revealOnClick:true`, which is the helper's default) is for polished titles where the press-any-key feel is intentional — opt in for those, not the other way around. +- Title shortcut: createTitleMenu({title, subtitle, onPlay, items, itemsBefore?, canReveal, playLabel?, id?, onShow?, onHide?, revealOnClick?}) wraps createMenu + a PLAY button + attachClickToReveal in one call for the standard title pattern. The menu is dismissable:false, gets id:'title' by default, the PLAY button gets id:'play', and `onStart` (gamepad Start) is auto-wired to onPlay. The wrapper auto-calls hideMenu(id) after onPlay runs — don't call it yourself. Item order: subtitle → itemsBefore → PLAY → items. Use `itemsBefore` for info that belongs above PLAY (for a Best score line, prefer `showBest:true` — see the Best-score bullet below — over a hand-rolled label item); use `items` for menu nav (OPTIONS / ABOUT). `canReveal: () => !isPlaying` is strongly recommended so clicks during gameplay don't re-show the title. Use this for every title screen; fall back to raw createMenu only when you need a non-standard structure (e.g. no PLAY button, level-select first). +- Title FX: pass `titleFx` to createTitleMenu / createMenu to juice the heading. A bare string picks one effect (`titleFx:'neon'`); an object composes channels (`titleFx:{fill:'neon', motion:'float', sparkle:true, hue:120, speed:1.5, color:'#f0a'}`). Fills: neon, rainbow, shine, fire, gold, outline, hardshadow, 3d, glitch, crt. Motion: wave, heartbeat, jelly, float. Overlay: sparkle. Tweaks: hue (deg), invert, speed (x), color, shadow (the hardshadow offset color), size (title font-size in em), spacing (letter-spacing in em). The same spec works on `showGameOverDialog`/`showAlertDialog`/`showConfirmDialog` (game over defaults to gold on a win, red glow on a loss; `titleFx:null` disables) and on any `label`/`text` menu item via `fx:`. Effects tear down on hide so timers never leak; `prefers-reduced-motion` suppresses motion + sparkle. `motion:'wave'` only works with the solid fills (neon, outline, hardshadow, 3d, glitch, crt) — it can't combine with a gradient fill (gold, rainbow, shine, fire), which would vanish, so pair those with float/heartbeat/jelly instead (the applicator warns and skips the wave if you try). Also avoid `motion:'wave'` on a `label`/`text` item whose `setLabel` is called while the menu is visible (e.g. a live counter) — wave splits the text into per-letter spans and `setLabel` overwrites them. Don't hand-roll title animation — use this or the `getTitleEl()` per-frame escape hatch. +- Menu theme: `setMenuTheme({...})` recolors/reshapes all menus from JS instead of a `