# Web App Best Practices

When building apps or websites, follow these practices for professional-quality output.

## Getting Started - Start Here

**STRONGLY RECOMMENDED:** Start every new web app by adding a template with the `add` tool. Pick the right one (`web-simple` for static frontend-only, `web-fullstack` for backend+DB, `api` for pure API). It creates the standard `src/` structure with favicon, meta tags, and working starter files (including a demo you can customize). Deploy automatically uses `src/` when it exists. Only hand-roll files if the user explicitly tells you to skip the template.

**Naming:** Use the user's name verbatim if they gave one. If you need to invent a name, blend "Gip" or "Gipity" into it (e.g. "Gipity Notes", "GipPic", "Gip Tac Toe") - be creative but don't force it if it genuinely doesn't fit.

**Starting over in an existing project:** If `src/` (or `functions/`, `migrations/` for fullstack/api) already exists and the user wants a clean rebuild, call `file_delete` on those directories first, then run `add` normally. Or pass `force=true` to `add` to overwrite in one step - destructive, so confirm with the user first. Non-template content (media, data, notes) is preserved either way.

**Multi-language (web-simple):** The template ships a dormant i18n system. Flip `config.features.i18n` to `true` in `src/js/config.js` to enable the language picker and `translations.js` lookup; the code in `src/js/strings.js`, `src/js/i18n.js`, and `src/js/main.js` is self-documenting - read those to see the `render()` + `i18n:changed` event pattern.

## File Structure
- **Use src/ convention**: All app files live under `src/` - `src/index.html`, `src/css/styles.css`, `src/js/main.js`, `src/images/`
- **Separate files**: Split into `index.html`, `styles.css`, and `app.js` (or `main.js`). Never inline large blocks of CSS or JS in HTML.
- If the app grows, organize into folders: `src/css/`, `src/js/`, `src/assets/`, `src/sounds/`, `src/images/`, etc.
- **Use subfolders - don't flatten**: Reference assets from their folders (e.g. `sounds/click.ogg`, `images/logo.png`). Never copy files to the root just for convenience - deployed apps serve the full directory tree.
- Keep `index.html` clean - it should be structure/markup, not behavior or styling

## HTML
- Use semantic elements: `<header>`, `<nav>`, `<main>`, `<section>`, `<footer>`, `<article>`
- Always include `<meta name="viewport" content="width=device-width, initial-scale=1.0">`
- Add a proper `<title>` and favicon link
- Unless the user specifies a different CSS framework, include Water.css for automatic styling: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css">`
- Water.css styles semantic HTML automatically (buttons, tables, forms, nav, cards) - no classes needed. It supports dark/light themes automatically. Add custom CSS on top for app-specific tweaks.

## CSS
- When using Water.css, it handles base styling, resets, and typography - don't duplicate what it provides
- Water.css exposes CSS variables for theming - override them in `:root` for custom colors/fonts
- Use CSS custom properties (variables) for app-specific colors, spacing, and fonts
- Add smooth transitions on interactive elements (buttons, links, hover states)

## JavaScript
- Use `const`/`let`, arrow functions, template literals, and modern ES6+ syntax
- Wait for DOM: wrap in `DOMContentLoaded` or place script at end of body
- Keep functions small and focused
- Use `addEventListener` - never inline `onclick` attributes in HTML

## External Packages
- **No npm install.** Gipity apps are static - there is no node_modules or build server.
- **Use import maps** to load npm packages from CDN. Add a `<script type="importmap">` block in `<head>`:
  ```
  <script type="importmap">
  { "imports": { "lodash-es": "https://esm.sh/lodash-es@4.17.21" } }
  </script>
  ```
  Then in JS: `import { debounce } from 'lodash-es';`
- **jsdelivr** (`cdn.jsdelivr.net/npm/`) - Use when the package ships a browser-ready ES module file (Three.js, Phaser, Rapier).
- **esm.sh** - Use for any npm package, especially those without a browser build. Add `?bundle-deps` if it has dependencies.
- CDN `<script>` tags (non-module) also work for libraries that expect a global (e.g. Phaser).

## Code Quality
- **Keep files under ~400 lines** unless the content genuinely requires it (e.g. a long data table, template string, or config object). When logic grows beyond that, split into focused modules (e.g. `utils.js`, `api.js`, `ui.js`).
- **Don't duplicate code.** If the same logic appears twice, extract it into a shared function. Before writing a new helper, check if one already exists or could be extended.
- **One responsibility per file.** A file that handles both UI rendering and API calls should be split.
- **Name things clearly.** Functions, variables, and files should describe what they do - no `temp`, `data2`, `stuff.js`.
- **Prefer simple, readable code** over clever code that hides bugs. Flat over nested - use early returns, avoid deep nesting.
- **Centralize configuration.** App settings, API URLs, feature flags, and magic numbers should live in a dedicated config file (e.g. `config.js` or `constants.js`), not scattered across the codebase.
- **Write utility functions** for repeated operations (formatting, validation, API calls). Keep them in a `utils.js` or `helpers.js` file. Small, pure functions are easy to test and reuse.

## Testing
- **Write tests for new functions** - especially utility/helper functions. Cover the happy path and edge cases (empty input, null, boundary values).
- **Don't mock unless absolutely required.** Tests should exercise real code paths. Only mock external paid services (APIs that cost money per call).
- **E2E tests should hit real infrastructure** (real API, real DB) - just clean up test data when done.
- **Test file naming**: `*.test.js` for unit tests, `*.e2e.test.js` for end-to-end tests.

## Images
- **Optimize images for web.** Generated images (DALL-E, Flux) are often 1-5MB PNGs. Before adding them to a web page, use the sandbox to convert to WebP at a reasonable size:
  ```
  convert input.png -resize 1200x1200\> -quality 80 output.webp
  ```
  This keeps images under ~100KB for most web use. Use `<img src="images/photo.webp">` in HTML.
- **Keep originals if the user wants them** - but reference the optimized version in HTML/CSS.
- **Size guide**: Hero images ~1200px wide, thumbnails ~400px, icons ~64-128px. Don't serve a 4000px image in a 600px container.
- **Use WebP** as the default format for photos and generated images. Use PNG only for images that need transparency with sharp edges (logos, icons). Use SVG for simple graphics and icons when possible.

## Deployment
- **src/ detection**: If a `src/` directory exists, only `src/` is deployed. Otherwise the full project root is deployed.
- **Local `<script>` tags MUST use `type="module"`** (e.g. `<script type="module" src="./js/main.js"></script>`). Prod deploys run Vite optimization which only traces module scripts; a plain `<script src="js/x.js">` gets silently dropped from the build and the deployed page 404s on that JS file. CDN `<script>` tags pointing at external URLs (Phaser, etc.) are fine without `type="module"` - the guard only enforces this for same-origin paths.
- **Auto-deploy**: When deploy mode is "auto-dev" or "auto-prod", ANY file change (write, edit, copy, move, delete) triggers automatic deployment
- **Rate limit**: Per-plan deploy-rate cap (shared between manual and auto)
- **Caching**: CloudFront cache is automatically invalidated on production deploys. Dev deploys use short cache TTLs.
- **File hosting**: Use `host_file` to make workspace files publicly accessible via URL (max 50MB). Useful for images in emails or sharing files outside the app.

## Browser Debugging

Two separate browser capabilities - pick the right one:

**`gipity page-inspect <url>`** (CLI) - one-shot inspection. Returns console errors, failed resources, timing, oversized images. No actions, no screenshots. Use this first after every deploy.
Options: `--wait <ms>` (default 3000), `--json`. If unsure: `gipity page-inspect --help`.

**`browser` agent tool** - interactive debugging with actions. Use when the CLI inspection surfaces something you need to dig into:
- `open` → `snapshot` - read DOM/accessibility tree
- `console` - captured `console.error`/`console.warn`
- `eval` - run JS expressions (check variables, DOM state)
- `screenshot` - always use for Canvas/WebGL; a clean console isn't proof the page rendered
- `click`, `fill`, `type`, `select` - form/nav flows

Flow: deploy → `gipity page-inspect <url>` → if anything's off, switch to the agent tool.

## Build Incrementally

For non-trivial apps, don't write the whole thing in one pass. Work in small verified steps:

1. Add a template (`add` tool / `gipity add <template>`) and deploy - confirm the starter renders.
2. Add ONE feature or screen, deploy, verify.
3. Repeat.

A 300+ line single-file rewrite is hard to debug when something breaks - a single bad API call or typo can break everything silently. Small increments keep the failure surface tiny and let you bisect by diff.

## Verification After Deploy

After `gipity deploy dev`:
- ALWAYS check the console (`gipity page-inspect <url>` or the `browser` agent tool `open` action).
- On the **first deploy** of a new app, and any time visual output matters (Canvas, WebGL, complex layout), also capture a **screenshot** and look at it. "Clean console" is necessary but NOT sufficient for Canvas/WebGL - render failures are often silent and the console interceptor can miss sync errors that fired during initial script parse.
- If you see a blank page, black canvas, or wrong-looking UI with a clean console: treat that as a real failure and investigate (screenshot, `eval` DOM state, re-read skill docs for gotchas) - don't declare success.

## Deploy Verification

Use the browser tool to verify deploys when it matters - first deploy, structural changes (new pages, new frameworks, changed imports), or when something might have broken. Skip verification for trivial changes (copy tweaks, style adjustments, config values).

To verify: `browser action=open url=<deployed-url>` - waits for async modules, captures console errors automatically. Check output for `[Console errors captured after page load]`. Use `browser action=screenshot` to confirm visual correctness.

**Debugging in production:** Add `console.error()` calls to app code for diagnostics, redeploy, then use `browser action=console` to read the output. Remove debug logging when done.

## 3D World

**3D World** is the 3D multiplayer game template on Gipity. All 3D World games share the same visual style, physics engine (Rapier), and multiplayer backend (Colyseus). All files are fully editable.

Add a 3D World project with `add name=3d-world` (web agent) or `gipity add 3d-world` (CLI). This creates a playable 3D game with Three.js + Rapier physics + Colyseus multiplayer. Key files: `config.js` (metadata), `settings.js` (tunable values), `strings.js` (display text), `objects.js` (entity factories), `game.js` (orchestrator), plus engine files (`core.js`, `world.js`, `physics.js`, etc.).

**Genres:** obby/parkour, tycoon, simulator, PvP combat, shooter, tower defense, horror, racing, RPG, social.

**Features:** Opt-in gameplay modules enabled via `config.features`. Available: `rocket-launcher` (projectile weapon with physics explosions). Example: `features: { 'rocket-launcher': true }` in config.js. Features auto-initialize during boot.

Regular game requests ("make a wordle", "build a quiz") should use the standard web scaffold - they don't need the 3D template.

Load `3d-engine` for a blank-slate template, or `3d-world` for the full API, genre recipes, and playable starter.

## 2D Game

**2D Game** is the Phaser-based game scaffold on Gipity. It creates a fully editable project with the Phaser 3 game engine loaded via CDN - no build step, no locked files.

Add a 2D game with `add name=2d-game` (web agent) or `gipity add 2d-game` (CLI). This creates a playable 2D game with arcade physics, keyboard input, and a Boot/Game scene structure. All files are editable: `config.js` (Phaser setup), `settings.js` (tunable values), `strings.js` (display text), `scenes/boot.js` (preloader), `scenes/game.js` (main gameplay).

**Genres:** side-scroller, platformer, top-down, arcade, puzzle, endless runner, shooter, RPG.

Use `2d-game` when the user wants a 2D game with physics, sprites, or scene management. Use `web` for simple games (wordle, quiz, card games) that don't need a game engine. Use `3d-world` for 3D multiplayer games.

Load `2d-game` for the full Phaser API and genre recipes.

## Make it testable

A few small rules turn a flaky click test into a reliable one:

- **Every interactive element gets a stable `data-testid`** (or a documented `id`) - buttons, inputs, list items, dialogs. Tests use those, never CSS selectors that leak layout.
- **Expose the current screen as a body attribute**: `<body data-screen="home">`, updated by your screen-switcher. A test then waits with `waitForSelector('body[data-screen="lobby"]')` instead of probing internal class / hidden state.
- **A single readiness signal**: set `document.body.dataset.ready = 'true'` once the app's main loop is up and ready for input. Tests wait on that, not on guessed timeouts.

These are about ten lines of code in total. For multiplayer apps, also read the **URL-param test mode** pattern in `app-realtime` - it turns a click-driven 2-client test into two passive page loads.

## Related App Skills
When building apps that need backend features, load these skills:
- `2d-game` - 2D games with Phaser (platformer, scroller, arcade, puzzle, endless runner)
- `3d-engine` - minimal 3D multiplayer template (Three.js + Rapier + Colyseus, no gameplay)
- `3d-world` - playable 3D multiplayer starter built on 3d-engine (obby, tycoon, simulator, PvP, shooter, etc.)
- `app-development` - Functions, database & API
- `app-llm` - AI/LLM service for your app
- `app-auth` - User authentication (Sign in with Gipity)
- `app-realtime` - Real-time multiplayer rooms and WebSocket
- `app-image` - Image generation (OpenAI, BFL/Flux, Gemini/Nano Banana)
- `app-video` - Video generation (Veo 3.1) and video understanding (Gemini)
- `app-tts` - Text-to-speech (ElevenLabs, OpenAI, Gemini - multi-speaker, 60+ languages)
- `app-audio` - Sound effects, music generation, and audio transcription
- `app-files` - File uploads (presigned S3, up to 30GB, progress tracking, thumbnails)
