diff --git a/features/enhancements/047-threejs-game/checklists/requirements.md b/features/enhancements/047-threejs-game/checklists/requirements.md new file mode 100644 index 00000000..8198b9cb --- /dev/null +++ b/features/enhancements/047-threejs-game/checklists/requirements.md @@ -0,0 +1,39 @@ +# Specification Quality Checklist: Three.js Game + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-05-14 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) + - Note: Spec references Three.js, R3F, drei, and DaisyUI by name. This is unavoidable because the feature is _defined by_ the choice of WebGL via R3F — there is no technology-neutral way to specify "render a 3D scene that integrates with the existing 32-theme system." The PRP at `047_threejs-game_feature.md` made the same choice. Acceptable trade-off. +- [x] Focused on user value and business needs +- [x] Written for non-technical stakeholders (modulo the unavoidable WebGL/Three.js terms above) +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable (every SC-\* has a quantitative or observable target) +- [x] Success criteria are technology-agnostic (SC items describe outcomes — render time, theme update, animation absence, build success, bundle size — not implementations) +- [x] All acceptance scenarios are defined (US-1 through US-5 each have Given/When/Then scenarios) +- [x] Edge cases are identified (WebGL unavailable, GPU context loss, runtime preference toggles, theme switch during animation, Pa11y exclusion regression, dice game regression, static export at build) +- [x] Scope is clearly bounded (Out of Scope section enumerates 8 explicit exclusions) +- [x] Dependencies and assumptions identified (Assumptions section + Dependencies section both present) + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria (FR-_ map cleanly to US-_ acceptance scenarios) +- [x] User scenarios cover primary flows (P1 = visit + theme; P2 = reduced motion + Pa11y; P3 = mobile) +- [x] Feature meets measurable outcomes defined in Success Criteria (each SC-\* is independently verifiable) +- [x] No implementation details leak into specification beyond the unavoidable WebGL/R3F naming + +## Notes + +- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan`. +- All items pass on first iteration; no rework required. +- `/speckit.clarify` completed 2026-05-15 — 4 questions resolved (v1 scene content, fallback UX, camera control bounds, analytics scope). All 11 taxonomy categories now Clear. Spec amended with FR-008 (fallback panel), NFR-006 (observability scope), expanded FR-005 (camera constraints + auto-orbit), and SC-009/SC-010 (fallback + auto-orbit success criteria). +- Wireframe gate per Constitution v1.0.2 Principle III completed 2026-05-15. Two wireframes generated and approved: `01-game-3d-main.svg` and `02-game-3d-fallback.svg`. Both PASS the v5.0 validator (zero errors). Twelve patch-classified issues resolved across the two SVGs (signature alignment, callout positioning to avoid button overlap, callout count parity with annotation groups, US-badge minimum, XML hygiene). Audit trails preserved at `wireframes/01-game-3d-main.issues.md` and `wireframes/02-game-3d-fallback.issues.md`. Spec.md `## UI Mockup` block records the sign-off. +- `/speckit.plan` is unblocked. diff --git a/features/enhancements/047-threejs-game/spec.md b/features/enhancements/047-threejs-game/spec.md new file mode 100644 index 00000000..27b7a253 --- /dev/null +++ b/features/enhancements/047-threejs-game/spec.md @@ -0,0 +1,244 @@ +# Feature Specification: Three.js Game + +**Feature Branch**: `047-threejs-game` +**Created**: 2026-05-14 +**Status**: Draft +**Input**: User description: "An interactive Three.js scene at /game/3d that demonstrates ScriptHammer's capacity for WebGL/3D content as a PWA. Built with @react-three/fiber and @react-three/drei. Theme-aware via DaisyUI CSS custom properties (32 themes), respects prefers-reduced-motion, static-export-compatible (no SSR for canvas), procedural geometry only for v1. Coexists with the existing dice game at /game (must not regress feature 037-game-a11y-tests). 5 user scenarios, 7 functional + 5 non-functional requirements." + +--- + +## Clarifications + +### Session 2026-05-15 + +- Q: What is the canonical v1 scene content? → A: ScriptHammer-themed sculpt: stylized procedural hammer + anvil + DaisyUI-themed accents (procedural only — no .glb imports). +- Q: WebGL-unavailable / GPU-context-lost fallback UX? → A: Static themed illustration (CSS/SVG hammer+anvil silhouette) + explanatory message + retry button. No auto-retry on context loss; user clicks retry explicitly. +- Q: How constrained should camera orbit be? → A: Constrained polar angle (no flipping under ground plane), 360° yaw, bounded zoom (min/max distance), AND auto-orbit when idle (suspends on user input, resumes after 3s of inactivity). Auto-orbit MUST disable when `prefers-reduced-motion: reduce` is set (already covered by FR-004). +- Q: Should /game/3d emit custom analytics events? → A: Page view only (rely on GA4's default page view). No custom scene-loaded or scene-interaction events for v1. Defer richer measurement to a follow-up feature if/when needed. + +## UI Mockup + +Approved wireframes (signed off 2026-05-15 by `/speckit.wireframe.review` after validator PASS): + +- [`wireframes/01-game-3d-main.svg`](wireframes/01-game-3d-main.svg) — desktop + mobile of `/game/3d` with canvas mounted. Anchors US-001, US-002, US-003, US-005, FR-002, FR-003, FR-004, FR-005, FR-007, NFR-004, SC-001, SC-002, SC-010. +- [`wireframes/02-game-3d-fallback.svg`](wireframes/02-game-3d-fallback.svg) — desktop + mobile of `/game/3d` fallback panel (WebGL unavailable OR `webglcontextlost` event fired). Anchors US-001, US-002, US-004, FR-008, SC-006, SC-009. + +Wireframe gate per Constitution v1.0.2 Principle III is now PASSED. `/speckit.plan` is unblocked. + +--- + + + +## Implementation Status + +**Last audited**: 2026-05-14 +**Real status**: Not Started +**Tracking**: GitHub issue #48; Phase 0.5 per `~/.claude/plans/gleaming-kitten-execution.md` + +### Shipped + +- (none yet) — this is a new route at `/game/3d` with no prior code. + +### Gaps + +- No `src/app/game/3d/` route directory. +- No 3D scene components under `src/components/game/`. +- No Pa11y exclusion for `/game/3d` in `config/pa11yci.json`. +- Three.js / R3F / drei not in `package.json` dependencies yet. + +### Notes + +- Existing dice game at `src/app/game/page.tsx` is unchanged and remains the target of feature `037-game-a11y-tests`. This feature creates a sibling sub-route at `src/app/game/3d/page.tsx`; the two coexist as independent Next.js route segments and must not interfere. +- This is the first feature to demand a documented Pa11y exclusion. The rationale (canvas content cannot be audited by Pa11y/axe-core; manual a11y review required) becomes precedent for future canvas/video features. + + + +## User Scenarios & Testing _(mandatory)_ + +### User Story 1 - Visit the 3D Game Route (Priority: P1) + +A user navigates to `/game/3d` and sees an interactive Three.js scene render after a brief loading state. They can rotate and zoom the camera via mouse, trackpad, or touch. + +**Why this priority**: This is the minimum viable demonstration of WebGL/3D capability. Without it, every other user story is moot. Ships independently as a static-only experience even if theme reactivity (US-2) and reduced motion (US-3) are deferred. + +**Independent Test**: Visit `/game/3d` in a desktop browser, observe a `` element renders 3D content within 2 seconds, and confirm the camera responds to drag gestures via orbit controls. + +**Acceptance Scenarios**: + +1. **Given** a user navigates to `/game/3d`, **When** the page hydrates, **Then** a loading spinner displays until the canvas mounts +2. **Given** the canvas has mounted, **When** the scene initializes, **Then** a `` element renders the v1 scene content (stylized procedural hammer + anvil + DaisyUI-themed accents — no `.glb`/`.gltf` imports) +3. **Given** the scene is rendering, **When** the user drags or scrolls, **Then** the camera responds via orbit controls +4. **Given** a fresh visit, **When** the production static export is served, **Then** the page works end-to-end with no server runtime (no `/api/` routes) + +--- + +### User Story 2 - Theme-Aware 3D Scene (Priority: P1) + +The 3D scene's colors, lighting, and materials reflect the currently active DaisyUI theme, and update in real time when the user switches themes via the existing ThemeSwitcher. + +**Why this priority**: The differentiator for this feature is that 3D content lives inside the same theme system as the rest of the app — not a hardcoded palette that breaks coherence with the 32-theme HTML chrome. Without theme reactivity, the 3D scene becomes a visual island; with it, the scene proves that ScriptHammer's theme system extends past the DOM. + +**Independent Test**: Load `/game/3d`, change the DaisyUI theme via the existing ThemeSwitcher, and observe scene colors update immediately without a page reload. + +**Acceptance Scenarios**: + +1. **Given** the user changes the DaisyUI theme via the ThemeSwitcher, **When** the scene re-renders, **Then** scene background, lights, and material colors update to match the new palette within one frame +2. **Given** a dark DaisyUI theme is active (e.g., `dark`, `dracula`, `night`), **When** the scene renders, **Then** background and lighting reflect dark-mode aesthetics +3. **Given** the scene reads DaisyUI CSS custom properties (`--p`, `--s`, `--b1`, etc.), **When** the `data-theme` attribute changes on ``, **Then** a `MutationObserver` triggers re-extraction (precedent: `useMapTheme` in `src/utils/theme-utils.ts`) + +--- + +### User Story 3 - Respect Reduced Motion (Priority: P2) + +Users who have set `prefers-reduced-motion: reduce` at the OS level see a static or low-motion version of the scene. User-initiated motion (manual camera orbiting) still works. + +**Why this priority**: Accessibility baseline (WCAG Success Criterion 2.3.3, Animation from Interactions). Required for the template's a11y-first stance. Could ship after US-1 + US-2, but cannot ship without before any release that markets the feature publicly. + +**Independent Test**: Set the OS `prefers-reduced-motion` preference to `reduce` (or use Chrome DevTools' rendering emulation), load `/game/3d`, and confirm auto-rotation and idle animations are disabled. Then drag the camera and confirm user-initiated orbiting still works. + +**Acceptance Scenarios**: + +1. **Given** OS-level `prefers-reduced-motion` is `reduce`, **When** the scene loads, **Then** auto-orbit (FR-005), idle animations, and any autonomous transitions are disabled +2. **Given** reduced motion is enforced, **When** the user manually orbits the camera, **Then** user-initiated motion still works (the preference scopes to autonomous animation, not user input) +3. **Given** the user toggles their OS preference at runtime, **When** the scene reads the media query, **Then** the animation state updates accordingly without requiring a page reload (precedent: commit `acb1920` — `feat(a11y): batch 6 — respect prefers-reduced-motion in game animations`) + +--- + +### User Story 4 - Pa11y Exclusion Documented (Priority: P2) + +`/game/3d` is excluded from automated Pa11y a11y scans because canvas content cannot be audited by Pa11y/axe-core. The exclusion is explicit in `config/pa11yci.json` and the rationale is recorded so future contributors don't try to re-include it. + +**Why this priority**: Without the exclusion, CI fails on every PR that ships this feature (Pa11y flags canvas as inaccessible). The pre-existing dice game at `/game` must retain its coverage via feature `037-game-a11y-tests` — the exclusion must scope to `/game/3d` only, not the entire `/game/*` subtree. + +**Independent Test**: Run Pa11y CI locally with the exclusion in place and confirm `/game/3d` is skipped while `/game` is still scanned and passes. + +**Acceptance Scenarios**: + +1. **Given** Pa11y CI runs, **When** it scans configured routes, **Then** `/game/3d` is skipped via `config/pa11yci.json` exclusion (and only `/game/3d` — `/game` is not skipped) +2. **Given** the exclusion exists, **When** a contributor reads `config/pa11yci.json`, **Then** they find a comment or sibling-doc reference recording the rationale (canvas not Pa11y-auditable; manual a11y review required) +3. **Given** the existing dice game at `/game`, **When** Pa11y runs, **Then** `/game` retains coverage via feature `037-game-a11y-tests` (no regression on existing automated scans) + +--- + +### User Story 5 - Mobile-Responsive Canvas (Priority: P3) + +The 3D scene resizes responsively, supports touch input for camera orbiting, and remains performant on a mid-tier mobile device. + +**Why this priority**: Mobile coverage is a baseline expectation for the template, but the v1 demo can ship desktop-first and add mobile polish in a follow-up if needed. P3 because US-1 alone delivers value to ~50% of visitors; mobile polish unblocks the other ~50%. + +**Independent Test**: Open `/game/3d` on a mobile device (or DevTools mobile emulation), confirm the canvas fills the available width without overflow, and confirm touch drag rotates the camera. + +**Acceptance Scenarios**: + +1. **Given** a user views on a mobile viewport (≤768px), **When** the scene renders, **Then** the canvas fills the available content width without horizontal overflow +2. **Given** the user touches and drags on a touch device, **When** orbit controls are active, **Then** the camera orbits via touch input (R3F + drei OrbitControls handle this natively) +3. **Given** device pixel ratio varies across devices, **When** the scene renders, **Then** DPR is capped (e.g., `[1, 2]`) to balance fidelity and performance + +--- + +### Edge Cases + +- **WebGL unavailable**: When the browser does not support WebGL (very old browsers, restricted enterprise environments), the canvas MUST be replaced by a fallback panel containing: a static themed illustration (CSS/SVG hammer + anvil silhouette using DaisyUI tokens for color), an explanatory message naming WebGL as the requirement, and a "Retry" button that re-attempts canvas mount. No auto-retry — user-initiated only. +- **GPU context loss**: When the browser releases the WebGL context (memory pressure, tab backgrounded for too long), the scene MUST handle the `webglcontextlost` event by swapping to the same fallback panel described above. The "Retry" button re-creates the canvas and re-mounts the scene. No silent auto-retry. +- **Reduced motion toggled at runtime**: When the user changes the `prefers-reduced-motion` OS preference while `/game/3d` is open, the scene MUST reflect the new state without requiring a page reload. +- **Theme switched during animation**: When the user changes themes mid-animation, material color updates MUST NOT interrupt user-initiated camera motion or cause a visible jump. +- **Pa11y exclusion regression**: If a future PR accidentally removes the exclusion in `config/pa11yci.json`, CI MUST fail loudly so the regression is caught before merge — not silently re-introduce the canvas-isn't-auditable error. +- **Dice game regression**: Any change to `src/app/game/` that touches the Next.js routing layer MUST be verified against `features/testing/037-game-a11y-tests/` before merge. +- **Static export at build time**: `next build` MUST succeed without invoking server-only Three.js code paths; the build MUST emit `out/game/3d/index.html` and only client-side JS chunks. + +## Requirements _(mandatory)_ + +### Functional Requirements + +- **FR-001**: System MUST serve the route `/game/3d` rendering a `` element after a dynamic client-only import (no SSR for the canvas surface). +- **FR-002**: Scene MUST reflect the current DaisyUI theme on initial render by reading CSS custom properties (`--p`, `--s`, `--b1`, etc.) from `:root`. +- **FR-003**: Scene MUST re-extract DaisyUI colors and update materials/lights when the `data-theme` attribute on `` changes (via `MutationObserver`, following the `useMapTheme` precedent). +- **FR-004**: Scene MUST disable all autonomous animations (auto-orbit, idle bobbing, ambient rotation of accent objects, etc.) when `prefers-reduced-motion: reduce` is set; user-initiated camera motion remains enabled. +- **FR-005**: Camera orbit controls MUST work via mouse, trackpad, and touch input (covered natively by drei's `OrbitControls`), with the following constraints: + - **Polar angle constrained** so the user cannot flip under the ground plane (approximately `Math.PI / 2` upper bound on `maxPolarAngle`). + - **Full 360° yaw** (azimuth unconstrained). + - **Bounded zoom** — explicit `minDistance` and `maxDistance` set on `OrbitControls` so the user cannot zoom into the geometry or zoom out into empty space. + - **Auto-orbit when idle** — the camera slowly rotates around the scene by default. User input (drag/scroll/touch) suspends auto-orbit immediately; auto-orbit resumes after 3 seconds of no input. Auto-orbit MUST be disabled when `prefers-reduced-motion: reduce` is set (per FR-004). +- **FR-006**: Route MUST be reachable via standard navigation (no auth required, no special headers, no payment gating). +- **FR-007**: Scene MUST use procedural geometry only for v1 — no `.glb`/`.gltf`/`.hdr` model or texture imports. The v1 scene content is a ScriptHammer-themed sculpt: a stylized procedural hammer and anvil with DaisyUI-themed accent objects (lights, ground plane, ambient decoration) — all built from primitive Three.js geometries (`BoxGeometry`, `CylinderGeometry`, `TorusGeometry`, etc.). Future asset-pipeline work is explicitly out of scope. +- **FR-008**: When WebGL is unavailable OR the `webglcontextlost` event fires, the route MUST display a fallback panel containing (a) a static themed illustration (CSS/SVG hammer + anvil silhouette using DaisyUI color tokens), (b) an explanatory message naming WebGL as the requirement, and (c) a user-actionable "Retry" button that re-attempts canvas mount. No silent auto-retry. + +### Non-Functional Requirements + +- **NFR-001**: Three.js + R3F + drei dependencies MUST be code-split to the `/game/3d` route — they MUST NOT inflate the homepage or other-route bundles. Verified via build output analysis. +- **NFR-002**: Initial scene paint MUST occur within 2 seconds on a mid-tier mobile device on a simulated 4G network (Lighthouse mobile profile). +- **NFR-003**: Static export (`next build` → `out/`) MUST succeed without runtime errors and MUST produce `out/game/3d/index.html`. +- **NFR-004**: Device pixel ratio MUST be capped (e.g., `[1, 2]`) to bound GPU cost on high-DPR mobile devices. +- **NFR-005**: `` MUST NOT be server-rendered (R3F is client-only). Achieved via `dynamic(() => import(...), { ssr: false })`. +- **NFR-006**: Observability scope is limited to GA4's default page view for `/game/3d`. No custom scene-loaded, scene-interaction, or theme-switched-in-scene events are emitted for v1. Privacy-friendly default per Constitution Principle VI; richer telemetry is deferred to a follow-up if engagement measurement becomes necessary. + +### Key Entities + +- **Scene**: The top-level R3F `` wrapper. Owns theme-aware color extraction, camera, lights, and the procedural geometry hierarchy. Contains the v1 ScriptHammer-themed sculpt (hammer + anvil + DaisyUI-themed accents). Re-renders on theme change. +- **Theme Tokens**: The DaisyUI CSS custom properties (`--p`, `--s`, `--b1`, etc.) read from `document.documentElement` at runtime. Converted to Three.js-compatible color values for materials and lights. +- **Reduced-Motion Preference**: The OS-level `prefers-reduced-motion` media query result, watched via `matchMedia`'s `change` event for runtime toggling. + +## Success Criteria _(mandatory)_ + +### Measurable Outcomes + +- **SC-001**: A user visiting `/game/3d` on a mid-tier mobile device on simulated 4G sees the scene render within 2 seconds (Lighthouse mobile profile). +- **SC-002**: Switching the DaisyUI theme via the existing ThemeSwitcher updates visible scene colors within one frame (≤16ms) on a mid-tier desktop. +- **SC-003**: With `prefers-reduced-motion: reduce` enforced, zero autonomous animations occur for 10 seconds of observation; user-initiated camera motion still works on every input modality (mouse, trackpad, touch). +- **SC-004**: Camera orbit works on every supported input modality with no input-method-specific bugs. +- **SC-005**: `next build` produces a static export containing `out/game/3d/index.html` with no SSR errors and no warnings about server-only code. +- **SC-006**: Pa11y CI completes successfully — `/game/3d` is skipped via the documented exclusion AND `/game` retains its prior coverage (no regression on feature `037-game-a11y-tests`). +- **SC-007**: Homepage and other-route bundle sizes are unchanged before vs. after this feature lands (Three.js bundle is route-split to `/game/3d` only). +- **SC-008**: Component structure validation (`pnpm run validate:structure`) passes for all new components under `src/components/game/`. +- **SC-009**: When WebGL is disabled in the browser (e.g., via Chrome flag `--disable-webgl`), the route renders the fallback panel (themed silhouette + message + retry button) within 2 seconds and the "Retry" button is keyboard-accessible. +- **SC-010**: With `prefers-reduced-motion: reduce` OFF, auto-orbit is observable within 3 seconds of idle and suspends within one frame of user input. + +## Assumptions + +- The user has a browser with WebGL 1.0+ support (effectively all browsers ≥ 2014; this is a hard prerequisite for Three.js). +- The user has a GPU capable of rendering basic Three.js scenes at interactive frame rates (true for all phones/tablets/laptops from the last ~5 years). +- The existing dice game at `/game` continues to be the target of `features/testing/037-game-a11y-tests/`. This feature does not modify or move the dice game. +- Pa11y / axe-core have no plausible roadmap for auditing canvas-rendered content in the v1 timeframe; the exclusion is therefore a sustained, not temporary, choice. +- The existing DaisyUI theme system (32 themes) remains stable for the v1 timeframe; if a future feature changes the CSS custom property naming (`--p`, `--s`, etc.), this scene's theme reactivity must be updated in lockstep. +- Procedural geometry alone is enough to demonstrate the WebGL/3D capability of the template; an asset pipeline for `.glb`/`.gltf` is explicitly deferred to a follow-up feature. + +## Out of Scope + +- Multiplayer or real-time sync (would require Supabase Realtime — separate feature). +- Leaderboards or persistent save state in Supabase (v1 has no backend persistence; localStorage may be used for ephemeral state only). +- Physics engine integration (`@react-three/rapier`, `cannon-es`). +- Audio or sound design. +- Asset pipeline for `.glb`/`.gltf`/`.hdr` model and texture imports. +- Payments, NFTs, or any Web3 integration. +- Promoting the 3D scene to the homepage hero or root route (separate IA decision). +- Replacing or moving the existing dice game at `/game`. +- Custom GA4 events beyond default page view (no scene-loaded, scene-interaction, or theme-switched-in-scene events for v1). + +## Dependencies + +- Constitution v1.0.2 mandatory wireframe gate between `/speckit.clarify` and `/speckit.plan` applies. Wireframes for desktop and mobile MUST be authored and reviewed before planning. +- Existing `useMapTheme` precedent in `src/utils/theme-utils.ts` for `MutationObserver`-based theme reactivity. +- Existing reduced-motion precedent: commit `acb1920` — `feat(a11y): batch 6`. +- Existing 5-file component pattern (mandatory per CLAUDE.md and constitution Principle I); enforced by `pnpm run validate:structure`. +- Pa11y CI config at `config/pa11yci.json`. + +## References + +### Internal + +- `src/app/game/page.tsx` — existing dice game; pattern for dynamic-import-no-SSR + two-column layout. +- `src/utils/theme-utils.ts` + `useMapTheme` hook — precedent for `MutationObserver`-based theme reactivity. +- `features/enhancements/021-geolocation-map/` — similar shape (heavy-canvas-with-controls feature); good template for spec/plan/wireframe artifacts. +- `features/testing/037-game-a11y-tests/` — existing a11y test feature targeting `/game` (must not regress). +- Commit `acb1920` — `feat(a11y): batch 6 — respect prefers-reduced-motion in game animations`. +- `features/foundation/006-component-template/` — mandatory 5-file pattern enforced by `validate:structure`. +- `features/foundation/001-wcag-aa-compliance/` — WCAG baseline (with documented Pa11y exclusion caveat for canvas content). +- `features/enhancements/047-threejs-game/047_threejs-game_feature.md` — original PRP that seeded this specification. + +### External + +- [Three.js documentation](https://threejs.org/docs/) +- [React Three Fiber documentation](https://r3f.docs.pmnd.rs/) +- [drei (R3F helpers) documentation](https://github.com/pmndrs/drei) +- [WCAG canvas accessibility guidance](https://www.w3.org/TR/html52/semantics-scripting.html#the-canvas-element) +- [WCAG 2.3.3 Animation from Interactions](https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html) diff --git a/features/enhancements/047-threejs-game/wireframes/01-game-3d-main.issues.md b/features/enhancements/047-threejs-game/wireframes/01-game-3d-main.issues.md new file mode 100644 index 00000000..1716612f --- /dev/null +++ b/features/enhancements/047-threejs-game/wireframes/01-game-3d-main.issues.md @@ -0,0 +1,35 @@ +# Issues: 01-game-3d-main.svg + +**Feature:** 047-threejs-game +**SVG:** 01-game-3d-main.svg +**Last Review:** 2026-05-15 +**Validator:** v5.0 + +--- + +## Summary + +| Status | Count | +| -------- | ----- | +| Open | 0 | +| Resolved | 3 | + +--- + +## Resolved Issues (2026-05-15 Review) + +### Signature + +| ID | Issue | Code | Classification | Resolution | +| ---- | ------------------------------------------------------------------------------------------------------------------------ | ------------- | -------------- | ---------------------------------------------------------------------------------------- | +| X-01 | Signature must be left-aligned at x=40, got x=960 | SIGNATURE-003 | PATCH | Moved signature `` from `x="960" text-anchor="middle"` to `x="40"` (left-aligned). | +| X-02 | Signature must NOT use `text-anchor="middle"` — use left-alignment at x=40 | SIGNATURE-003 | PATCH | Removed `text-anchor="middle"` from signature. | +| X-03 | Signature format wrong: `'047:01 \| Three.js Game - Main \| SpecKit'` — must be `NNN:NN \| Feature Name \| ScriptHammer` | SIGNATURE-004 | PATCH | Renamed trailing token `SpecKit` → `ScriptHammer` per project signature convention. | + +--- + +## Notes + +- All issues classified as PATCH per `features/CLAUDE.md` decision table (cosmetic/positional, not layout/spacing). +- Validator re-run after patches: **PASS** (zero errors). +- Auto-generated initially by validator v5.0; manually annotated with resolutions by `/speckit.wireframe.review` (2026-05-15). diff --git a/features/enhancements/047-threejs-game/wireframes/01-game-3d-main.svg b/features/enhancements/047-threejs-game/wireframes/01-game-3d-main.svg new file mode 100644 index 00000000..f6350e8b --- /dev/null +++ b/features/enhancements/047-threejs-game/wireframes/01-game-3d-main.svg @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + 3D GAME (THREE.JS SCENE) - /game/3d + + + DESKTOP (16:9) + MOBILE + + + + + + + + 3D Game (Three.js) + /game / 3d + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Drag to orbit · Scroll to zoom + + + + + Auto-orbit on + + + + Theme: winter · DPR: 2.0 · prefers-reduced-motion: no-preference + + + + + 1 + 2 + 3 + 4 + + + + + + + + + 3D Game + + + + + + + + + + + + + + + + + + + + + + + Pinch to zoom · Drag to orbit + + + + + Auto-orbit on (resumes after 3s idle) + + + DPR capped [1,2] · WebGL OK + + + + + 1 + 5 + + + + + + + + + + 1 + Canvas mount + orbit controls + US-001 + FR-005 + SC-001 + Dynamic-import-no-SSR. drei OrbitControls; + polar angle & zoom bounded; 360° yaw. + + + + + + 2 + Theme-aware materials + US-002 + FR-002 + FR-003 + SC-002 + Reads DaisyUI CSS custom props (--p, --s, + --b1); MutationObserver on data-theme. + + + + + + 3 + Auto-orbit + reduced motion + US-003 + FR-004 + SC-010 + Auto-orbit suspends on input, resumes after + 3s idle. Disabled under reduce-motion. + + + + + + 4 + Procedural sculpt only (no .glb) + US-001 + FR-007 + v1 = hammer + anvil + DaisyUI accent orbs. + Boxes, cylinders, tori only. No asset import. + + + + + + 5 + Mobile touch + DPR cap + US-005 + FR-005 + NFR-004 + Touch-drag orbits via drei. DPR capped + [1, 2] to bound GPU on high-DPR devices. + + + + + Status bar legend + Theme, DPR, and reduced-motion preference + surface in the canvas footer for debugging. + + + + + 047:01 | Three.js Game - Main | ScriptHammer + diff --git a/features/enhancements/047-threejs-game/wireframes/02-game-3d-fallback.issues.md b/features/enhancements/047-threejs-game/wireframes/02-game-3d-fallback.issues.md new file mode 100644 index 00000000..fbe2d7a7 --- /dev/null +++ b/features/enhancements/047-threejs-game/wireframes/02-game-3d-fallback.issues.md @@ -0,0 +1,61 @@ +# Issues: 02-game-3d-fallback.svg + +**Feature:** 047-threejs-game +**SVG:** 02-game-3d-fallback.svg +**Last Review:** 2026-05-15 +**Validator:** v5.0 + +--- + +## Summary + +| Status | Count | +| -------- | ----- | +| Open | 0 | +| Resolved | 9 | + +--- + +## Resolved Issues (2026-05-15 Review) + +### Callout positioning + +| ID | Issue | Code | Classification | Resolution | +| ---- | ----------------------------------------------------------------------------- | ----------- | -------------- | ------------------------------------------------------------------------------------------------------ | +| X-01 | Callout at cy=620 too close to desktop footer (y=640) — move up | COLL-001 | PATCH | Moved callout 3 from (1056,620) to (1180,568) — pushed up to status-bar level, away from footer. | +| X-02 | Callout at (640,524) overlaps button at (540,502) — place after (right/below) | CALLOUT-003 | PATCH | Moved callout 2 from (640,524) to (770,524) — now sits to the right of the desktop Retry button. | +| X-03 | Callout at (180,506) overlaps button at (80,484) — place after (right/below) | CALLOUT-003 | PATCH | Moved mobile callout 2 from (180,506) to (304,506) — now sits to the right of the mobile Retry button. | + +### Callout count vs annotation count + +| ID | Issue | Code | Classification | Resolution | +| ---- | --------------------------------------------------------------------------------------- | ----------- | -------------- | ----------------------------------------------------------------------------------------------------------------- | +| X-04 | Mockup missing 1 callout circles (annotation has 6 concepts, mockup only illustrates 5) | CALLOUT-002 | PATCH | Added callouts 4, 5, 6 on desktop mockup at distinct concept locations (silhouette context, body copy, headline). | + +### User Story badge minimum + +| ID | Issue | Code | Classification | Resolution | +| ---- | ------------------------------------------------------------- | ------ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| X-05 | Only 1 User Story badges found — need at least 3 User Stories | US-002 | PATCH | Added US badges to annotation groups 4, 5, 6 (US-001 — fallback as degraded visit path; US-004 — Pa11y baseline; US-002 — visual theme consistency). | + +### Signature + +| ID | Issue | Code | Classification | Resolution | +| ---- | -------------------------------------------------------------------------- | ------------- | -------------- | ------------------------------------------------------------------------- | +| X-06 | Signature must be left-aligned at x=40, got x=960 | SIGNATURE-003 | PATCH | Moved signature `` from `x="960" text-anchor="middle"` to `x="40"`. | +| X-07 | Signature must NOT use `text-anchor="middle"` — use left-alignment at x=40 | SIGNATURE-003 | PATCH | Removed `text-anchor="middle"`. | +| X-08 | Signature format wrong: `'047:02 \| Three.js Game - Fallback \| SpecKit'` | SIGNATURE-004 | PATCH | Renamed trailing token `SpecKit` → `ScriptHammer`. | + +### XML hygiene (regression introduced + fixed in same session) + +| ID | Issue | Code | Classification | Resolution | +| ---- | ------------------------------------------- | ------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| X-09 | Attribute 'x' has unquoted value '80..280,' | XML-004 | PATCH | Validator's regex parsed an XML comment containing `at x=80..280, y=...` as an attribute. Reworded the comment to drop the literals. | + +--- + +## Notes + +- All 9 issues classified as PATCH per `features/CLAUDE.md` decision table (cosmetic/positional/comment-text — no layout overlaps, no spacing, no missing sections requiring REGEN). +- Validator re-run after patches: **PASS** (zero errors). +- Auto-generated initially by validator v5.0; manually annotated with resolutions by `/speckit.wireframe.review` (2026-05-15). diff --git a/features/enhancements/047-threejs-game/wireframes/02-game-3d-fallback.svg b/features/enhancements/047-threejs-game/wireframes/02-game-3d-fallback.svg new file mode 100644 index 00000000..920fd34d --- /dev/null +++ b/features/enhancements/047-threejs-game/wireframes/02-game-3d-fallback.svg @@ -0,0 +1,205 @@ + + + + + + + + + + + + + 3D GAME (THREE.JS SCENE) - /game/3d (FALLBACK) + + + DESKTOP (16:9) + MOBILE + + + + + + + + 3D Game (Three.js) + /game / 3d + + + + + + + + + + + + + + + + + + + + + + + + + + 3D Content Unavailable + + + 3D content requires WebGL. Your browser does not support it, + or the graphics context was lost. + + + + + Retry + + + + No auto-retry — user-initiated only · 3D content excluded from Pa11y scans + + + + WebGL: unavailable · webglcontextlost: handled · keyboard: Retry focusable + + + + + 1 + 2 + 3 + 4 + 5 + 6 + + + + + + + + + 3D Game + + + + + + + + + + + + + + + + + + + 3D Content + Unavailable + + + WebGL is required. Your browser + does not support it, or the + graphics context was lost. + + + + + Retry + + + + User-initiated retry only. + No auto-retry on context loss. + + + WebGL: unavailable + + + + + 1 + 2 + + + + + + + + + + 1 + Themed silhouette fallback panel + FR-008 + SC-009 + CSS/SVG hammer + anvil silhouette using + DaisyUI color tokens. No canvas, no WebGL. + + + + + + 2 + Retry button - user-initiated only + FR-008 + SC-009 + 44×44 button, keyboard-focusable. Triggers + canvas re-mount. No silent auto-retry. + + + + + + 3 + Pa11y exclusion (manual a11y review) + US-004 + SC-006 + /game/3d excluded from Pa11y (canvas not + auditable). Fallback panel is auditable. + + + + + + 4 + When the fallback renders + US-001 + Triggers: (a) WebGL not available at mount, + (b) `webglcontextlost` event fires at runtime. + + + + + + 5 + Fallback is Pa11y-auditable + US-004 + Headings, body copy, Retry button: all + standard DOM. Pa11y exclusion scopes to canvas. + + + + + + 6 + Silhouette uses opacity to read as "off" + US-002 + 85% opacity + thin diagonal line cue convey + "3D unavailable" without alarming red iconography. + + + + + 047:02 | Three.js Game - Fallback | ScriptHammer + diff --git a/features/enhancements/047-threejs-game/wireframes/includes/README.md b/features/enhancements/047-threejs-game/wireframes/includes/README.md new file mode 100644 index 00000000..8ff53570 --- /dev/null +++ b/features/enhancements/047-threejs-game/wireframes/includes/README.md @@ -0,0 +1,103 @@ +# Wireframe Includes + +Reusable wireframe components with build-time injection. Update header/footer once, regenerate wireframes to see changes. + +## Architecture: Build-Time Injection + +**Why not runtime ``?** Nested external SVG references don't work - browsers can't resolve `` inside a cloned external group. + +**Solution:** `/wireframe` reads these files and **injects content directly** into generated SVGs. Icons are inline paths, not symbol references. + +## Files + +| File | Description | Group ID | +| -------------------- | ----------------------------------------- | ---------------------- | +| `defs.svg` | Master icon library (reference only) | N/A | +| `header-desktop.svg` | Desktop header (logo, nav, icons, avatar) | `#desktop-header` | +| `header-mobile.svg` | Mobile status bar + header | `#mobile-header-group` | +| `footer-mobile.svg` | Mobile bottom nav (4 tabs) | `#mobile-bottom-nav` | + +## How It Works + +1. `/wireframe` reads include files at generation time +2. Extracts content inside the `` group +3. Injects directly into the generated SVG +4. No external references at runtime + +``` +Include File Generated Wireframe +┌─────────────────────┐ ┌─────────────────────────────┐ +│ │ inject│ │ +│ │ │ │ +│ │ │ ← inline │ +│ │ │ │ +└─────────────────────┘ └─────────────────────────────┘ +``` + +## Updating Components + +To update all wireframes with new header/footer: + +1. Edit the include file (e.g., `header-desktop.svg`) +2. Run `/wireframe [feature]` to regenerate (or regenerate all) +3. Changes are embedded in the regenerated SVGs + +This maintains a **single source of truth** while ensuring icons always render. + +## Icons + +All icons are inline `` elements from Heroicons 24x24 solid. Icons embedded directly in include files - no `` to external files. + +### Icon Reference (from defs.svg) + +| Icon | Use Case | +| -------------- | ----------------- | +| `icon-cog` | Settings gear | +| `icon-eye` | Accessibility | +| `icon-home` | Home nav | +| `icon-bolt` | Features | +| `icon-doc` | Docs | +| `icon-user` | Account/profile | +| `icon-signal` | Mobile status bar | +| `icon-battery` | Mobile status bar | +| `icon-menu` | Hamburger menu | + +## Active States + +Headers and footers have all items **inactive by default**. To set an active nav item, overlay after the include: + +### Desktop Nav Active State + +```xml + + + + Home + +``` + +### Mobile Bottom Nav Active State + +```xml + + + + + + + Home + +``` + +## Colors Reference + +| Color | Hex | Use | +| ---------------- | --------- | -------------------------- | +| Primary | `#8b5cf6` | Brand, active states, CTAs | +| Dark text | `#1a1a2e` | Headings, icons | +| Body text | `#374151` | Regular text | +| Muted text | `#4b5563` | Secondary text | +| Parchment | `#e8d4b8` | Main background | +| Darker parchment | `#dcc8a8` | Headers, cards | +| Border | `#b8a080` | Strokes | diff --git a/features/enhancements/047-threejs-game/wireframes/includes/defs.svg b/features/enhancements/047-threejs-game/wireframes/includes/defs.svg new file mode 100644 index 00000000..94fe167a --- /dev/null +++ b/features/enhancements/047-threejs-game/wireframes/includes/defs.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/features/enhancements/047-threejs-game/wireframes/includes/footer-desktop.svg b/features/enhancements/047-threejs-game/wireframes/includes/footer-desktop.svg new file mode 100644 index 00000000..28568967 --- /dev/null +++ b/features/enhancements/047-threejs-game/wireframes/includes/footer-desktop.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + ScriptHammer + Product planning template + + + + + Features + Documentation + GitHub + Privacy + Terms + Built with Next.js 15, Supabase, and Tailwind CSS + + + + + + © ScriptHammer 2026 + MIT License + + + diff --git a/features/enhancements/047-threejs-game/wireframes/includes/footer-mobile.svg b/features/enhancements/047-threejs-game/wireframes/includes/footer-mobile.svg new file mode 100644 index 00000000..7c42313f --- /dev/null +++ b/features/enhancements/047-threejs-game/wireframes/includes/footer-mobile.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + Home + + + + + + + + + Features + + + + + + + + + + Docs + + + + + + + + + Account + + + + + diff --git a/features/enhancements/047-threejs-game/wireframes/includes/header-desktop.svg b/features/enhancements/047-threejs-game/wireframes/includes/header-desktop.svg new file mode 100644 index 00000000..056709af --- /dev/null +++ b/features/enhancements/047-threejs-game/wireframes/includes/header-desktop.svg @@ -0,0 +1,63 @@ + + + + + + + + + + ScriptHammer + + + + + + + + Home + + + + + Features + + + + + Docs + + + + + Account + + + + + + + + + + + + + + + + + + + U + + + diff --git a/features/enhancements/047-threejs-game/wireframes/includes/header-mobile.svg b/features/enhancements/047-threejs-game/wireframes/includes/header-mobile.svg new file mode 100644 index 00000000..6635ff23 --- /dev/null +++ b/features/enhancements/047-threejs-game/wireframes/includes/header-mobile.svg @@ -0,0 +1,64 @@ + + + + + + + + 9:41 + + + + + + + + + + + + + + + + + + + + + + + + + + + ScriptHammer + + + + + + + + + + + + + + + + + U + + + +