# Frontend Architecture Skill

**Use:** Component patterns, state mgmt, frontend structure.
**Related:** wordpress-plugin-foundation-skill.md (always applies)

---

## Module Pattern (IIFE)

Wrap all modules in IIFE with `'use strict'`. Private state inside closure. Expose public API via `window.PresszoneForumApp` or named return object. Auto-init: check `document.readyState === 'loading'` → listen `DOMContentLoaded`, else call `init()` directly.

Named module: assign IIFE result to `const PresszoneForumModule`. Guard `init()` with `if (state.initialized) return`.

---

## State Management

### Centralized (cross-module)

IIFE-wrapped `StateManager` with private `state` object and `listeners` array. `setState(updates)` spreads updates, notifies all listeners with `(newState, oldState)`. `subscribe(listener)` returns unsubscribe fn. `getState()` returns shallow copy.

### Component-level

Class with `this.state`. `setState(updates)` merges updates → calls `this.render()`. `render()` reflects state via class toggles (never inline styles).

---

## Component Pattern

Class-based. Constructor takes `element` or `options`. Methods: `render()`, `bindEvents()`, `open()`, `close()`. Escape user strings via `div.textContent = text; return div.innerHTML` — never `innerHTML` with user data directly. Remove event listeners in `close()`. Focus trap on open: query `button,[href],input,select,textarea,[tabindex]:not([tabindex="-1"])`, focus first, Tab/Shift+Tab wraps between first/last.

---

## Event Bus Pattern

IIFE with private `events` map. `on(event, cb)` registers, returns `off()` fn. `emit(event, data)` calls all registered cbs. `once(event, cb)` auto-removes after first fire.

---

## Module Communication

Use `document.dispatchEvent(new CustomEvent('fpz:event-name', { detail: {...} }))` for cross-module events. Listening modules use `document.addEventListener('fpz:event-name', handler)` in `init()`.

---

## Data Loading

### Lazy Loading
Class wrapping `IntersectionObserver`. `observe(element)` registers target. On intersection: fetch `element.dataset.src`, set `innerHTML`, add `loaded` class, unobserve. Catch errors.

### Infinite Scroll
Class with `loading`, `hasMore`, `page` state. `scroll` listener checks distance from bottom vs `threshold`. `loadMore()` guards with `if (this.loading) return`, increments page, calls `options.loadMore(page)`. Empty result → `this.hasMore = false`. Always `finally { this.loading = false }`.

---

## Form Management

Class `FormHandler(form, options)`. On submit: `e.preventDefault()`, clear errors, get `FormData`, validate, `setLoading(true)`, call `options.onSubmit(data)`, reset on success, show errors on catch. `getFormData()` builds plain object from `FormData.entries()`, handles multi-value keys as arrays. `setLoading` disables submit btn + toggles `is-loading` class. Errors rendered via `escapeHtml` into `.form-errors` container.

---

## Router Pattern (Hash SPA)

Class `Router`. `register(path, handler)` maps paths. `navigate(path)` sets `location.hash`. `handleRoute()` on `hashchange`: slice `#`, split path/query, parse query params, call registered handler or `/404`. `parseQuery` decodes key/value pairs.

---

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| Global state pollution | IIFE encapsulation |
| Leaked event listeners | Remove on component destroy |
| Memory leaks | Clear refs when done |
| Per-element event binding | Event delegation to parent |
| Mixed concerns | Separate data/UI/logic |
| Unhandled async errors | try-catch on all async |
| Blocking UI | async/await for long ops |
| Scroll/input spam | Debounce input, throttle scroll |

---

## Related Skills

- `wordpress-plugin-foundation-skill.md` — security/compliance
- `javascript-skill.md` — JS patterns/APIs
- `css-scss-skill.md` — component styling
- `accessibility-skill.md` — accessible components
