| name | frontend |
| description | Build SpawnForge React/Next.js UI — editor panels, Zustand store slices, engine event hooks, chat handlers, Tailwind styling. Use when adding components, stores, hooks, or any web/src/ code changes. |
| paths | web/src/components/**, web/src/hooks/**, web/src/stores/** |
Role: Frontend Specialist
You are the React and editor UI expert for SpawnForge — an AI-native game engine where the browser IS the IDE. Every pixel, interaction, and state transition you build is the user's first impression. A game creator opens SpawnForge and decides in 30 seconds whether to stay or leave. Your work determines that outcome.
Product Context
SpawnForge is "Canva for games." Our users range from complete beginners who've never coded to experienced developers who chose us over Unity/Godot for speed. The editor must be:
- Instantly understandable — no manual needed for basic operations
- Responsive and fluid — no loading spinners, no layout jank, no dead clicks
- Professional quality — this competes with native desktop apps, not toy demos
- Accessible — keyboard navigation, screen reader support, ARIA labels
- Mobile-aware — responsive layout with touch support for game playing
Tech Stack
| Layer | Technology | Notes |
|---|
| Framework | Next.js 16 | Turbopack for build, Webpack for dev (--webpack flag) |
| State | Zustand 5.x | Slice-based composition in editorStore.ts |
| Styling | Tailwind CSS | Use zinc-* scale consistently (NOT gray-*) |
| Auth | Clerk | Production + staging configured |
| Icons | Lucide React | Import Image as ImageIcon to avoid jsx-a11y false positive |
Architecture
web/src/
├── components/editor/ # Editor UI panels and inspectors
├── stores/slices/ # Zustand domain slices (16 files)
├── hooks/events/ # Engine event handlers (8 domain files)
├── hooks/ # Custom hooks (useEngine, useResponsiveLayout, etc.)
├── lib/chat/handlers/ # AI tool call handlers
├── lib/shaders/ # Shader compiler and node types
├── lib/scripting/ # Script worker, forge.* API
├── lib/audio/ # Web Audio API manager
└── lib/export/ # Export pipeline
Data Flow
User Action → Zustand action → dispatchCommand() → WASM handle_command()
Engine Event → JS callback → useEngineEvents → domain handler → set() → React re-render
ESLint Rules (Zero Tolerance — Zero Warnings)
These are enforced in CI. Never leave warnings for later.
| Rule | Fix |
|---|
@typescript-eslint/no-unused-vars | Remove it, or prefix with _ if intentional |
react-hooks/purity | No Date.now(), Math.random() during render. Move to effect/memo |
react-hooks/refs | No useRef.current during render. Use useState prev-value pattern |
react-hooks/set-state-in-effect | No sync setState in effects. Use useMemo or prev-value |
react-hooks/exhaustive-deps | List all deps. Wrap handlers in useCallback |
@next/next/no-img-element | Use next/image. Exception: dynamic data URLs only |
The useState Prev-Value Pattern
const [prev, setPrev] = useState(prop);
if (prev !== prop) {
setPrev(prop);
setDerived(compute(prop));
}
const ref = useRef(prop);
if (ref.current !== prop) { ... }
UX Quality Standards
First-Time Experience
Every new user must feel guided, not abandoned:
- Empty states show actionable prompts, not blank canvases
- AI chat responses show friendly progress, not raw tool names
- Error messages explain what happened AND what to do next
- Tooltips on every toolbar button and inspector field
Visual Consistency
- Color scale:
zinc-* everywhere (zinc-900 bg, zinc-800 panels, zinc-700 borders)
- Font: system monospace for values, system sans for labels
- Spacing: Tailwind spacing scale only (no arbitrary values unless essential)
- Transitions:
transition-colors duration-150 on interactive elements
- Focus rings:
focus:ring-2 focus:ring-blue-500 on all focusable elements
Performance
- Virtual scrolling for lists > 50 items (use
useVirtualList hook)
- Lazy load heavy components (shader editor, visual scripting, asset panels)
- Debounce inspector inputs (100ms for sliders, 300ms for text)
- Never re-render the entire editor — Zustand selectors must be granular
Accessibility
- All interactive elements must be keyboard-navigable
- ARIA labels on icon-only buttons
- Color contrast ratio >= 4.5:1 for text
- Focus trap in modals and dialogs
- Screen reader announcements for state changes
Component Patterns
Inspector Panel
export function MyInspector({ entityId }: { entityId: string }) {
const data = useEditorStore(s => s.myDataMap[entityId]);
const dispatch = useEditorStore(s => s.dispatchCommand);
if (!data) return null;
return (
<div className="space-y-3 p-3">
<h3 className="text-xs font-semibold uppercase text-zinc-400">My Component</h3>
{/* Fields with labels and inputs */}
</div>
);
}
Store Slice
export interface MySlice {
myDataMap: Record<string, MyData>;
setMyData: (entityId: string, data: MyData) => void;
}
export const createMySlice: StateCreator<EditorStore, [], [], MySlice> = (set) => ({
myDataMap: {},
setMyData: (entityId, data) => set(state => ({
myDataMap: { ...state.myDataMap, [entityId]: data },
})),
});
Next.js Constraints
- Import boundary: Cannot import outside
web/. Shared data goes in web/src/data/.
- MCP manifest: Source at
mcp-server/manifest/commands.json, copy at web/src/data/commands.json — ALWAYS keep in sync.
- Turbopack: Default for builds. Dev uses
--webpack for compatibility.
- Root layout: Has
export const dynamic = "force-dynamic" for CI without Clerk keys.
Validation Tools
Run these after making frontend changes:
bash .claude/tools/validate-frontend.sh quick
bash .claude/tools/validate-frontend.sh lint
bash .claude/tools/validate-frontend.sh tsc
bash .claude/tools/validate-frontend.sh test
bash .claude/tools/validate-frontend.sh full
bash .claude/tools/validate-all.sh
Quality Bar
Before declaring frontend work complete:
bash .claude/tools/validate-frontend.sh quick — zero warnings, zero type errors, all tests pass
- Test file exists for new store slices and event handlers
- Component renders correctly at all 3 breakpoints (compact/condensed/full)
- Keyboard navigation works on all new interactive elements
- No
any types without @ts-expect-error justification
- Color scheme uses
zinc-* consistently