# Frontend conventions checklist (quick reference)

The authoritative rules are [`client/CLAUDE.md`](../../../client/CLAUDE.md) and the **frontend-designer**
skill (invoke it for any visual work). This is a tick-list. If anything here seems to conflict with
those, **they win**.

## Boundaries & structure
- [ ] Never add a layout above `[locale]`; `src/app/[locale]/layout.tsx` is the root layout.
- [ ] Respect the RSC/client boundary: no `next/headers`, `next-intl/server`, or `@/lib/cookies/server`
      in client components; no `@/lib/cookies/client` in an RSC.
- [ ] New route group / provider / top-level `src/` folder ⇒ update the **Project Structure** tree in
      `client/CLAUDE.md`.

## Data fetching & caching (prevent extra fetches and re-renders)
- [ ] Fetch **only** through `clientFetch`/`serverFetch` (`@/lib/api`); domain calls live in
      `src/services/{domain}/apis/`. Never raw `fetch()`.
- [ ] Server state is **TanStack Query**: a `keys.ts` factory per domain, deliberate `staleTime`/`gcTime`,
      and **cache invalidation on mutation** (`invalidateQueries`/`setQueryData`) so you never refetch
      data you already hold. Prefer prefetch/`initialData` from RSC where it removes a client round-trip.
- [ ] One hook per file (`use{Action}.ts`), `useQuery`/`useMutation`. Don't toast 401/403/5xx in hooks
      (the fetch layer already does) — only domain-specific 4xx messages.
- [ ] Minimise re-renders: stable references (`useCallback`/`useMemo` only where it pays), `select` to
      subscribe to slices, colocate state low, lift only when shared. Don't put fast-changing state in
      a high context provider.

## UI composition
- [ ] **Concrete MUI primitives stay MUI** — use `Button`, `Avatar`, `Paper`, `TextField`, etc. (or the
      existing `App*` wrappers); never invent a new root-level Button/Avatar.
- [ ] **Composite/shareable components** (built from primitives, reused in >1 place — a nurse card, an
      OTP input, a price-breakdown, a step header) live at the right **shared** level (`src/components/…`),
      not inline in a page or buried in a leaf. A component imported from >1 place gets a co-located
      `*.test.tsx`.
- [ ] Page-only, never-reused composition can stay in the page.

## i18n, theme, direction
- [ ] Every user-visible string is a key in **both** `messages/en.json` and `messages/fa.json` (in sync).
      `fa` is default and **RTL** — design RTL-first; verify mirroring.
- [ ] Colours come from `tokens.css` (`var(--bal-…)` / brand tokens), never hardcoded in `sx`. Use the
      pre-built `APP_THEME_LTR/RTL`; never `createTheme()` in a component.
- [ ] MUI **v9** API only (`sx={{ mb: 4 }}`, no v5/v6-only props).

## Quality
- [ ] Magic strings → named constants (`src/constants/` or a colocated `constants.ts`).
- [ ] Cookies/app state only through the cookie manager; never `document.cookie`/`localStorage` for auth.
- [ ] No dead code (unused vars/imports are lint **errors**). Comment the *why*, not the *what*.
- [ ] `npm run check` green; `npm run test:ci` green when a shared component changed.
- [ ] Types come from the published contract in `dev/contracts/` — don't guess server shapes; if a shape
      is missing, append a request to `dev/shared-working-context/frontend/requests/for-backend.md` and
      mock behind the `services/{domain}` seam meanwhile.
