# UI prototype

Several **structurally different** variations of a screen, presented side by side, so the decision
gets made by looking rather than by imagining. Flip between them, pick one, or steal parts from each.

Where the question is about logic or state rather than appearance, this is the wrong branch. Use
[LOGIC.md](LOGIC.md).

## Pick the fidelity first

| Shape | Use it when | Cost |
|---|---|---|
| **Static mocks** (default) | The question is layout, hierarchy, or visual direction. Standalone HTML, no app involved. | Minutes. Zero risk to the app. |
| **In-app variants** | The question is how it behaves against **real** data, density, and chrome. A layout that looks fine on lorem ipsum falls apart on a real row count. | Higher. Touches the repo. |

**Start with static mocks.** They are the house default: several distinct standalone pages, served by
a simple static server, URL handed over. Escalate to in-app variants only where real data is
genuinely load-bearing to the judgement, and say why when you do.

## Static mocks (default)

Write each variant as a standalone HTML file in a scratch directory outside the repo, styled to match
the project's design language closely enough to judge (Tailwind via CDN is fine here, since nothing
ships).

Serve the directory with something already installed, such as `python3 -m http.server`, and hand over
the URL plus the filenames. Then stop.

Never edit a real component to answer a layout question.

## In-app variants

Render variants **on the route that already exists**, gated by a `?variant=` search param. The
existing data fetching, params, and auth all stay; only the rendered subtree swaps. A variant judged
inside the real header, sidebar, and data density is judged honestly; one in an empty route is judged
in a vacuum, where everything looks fine.

Where the thing has no page yet but would naturally live inside one (a new dashboard section, a new
card on settings, a new step in a flow), mount the variants inside that host page. Only create a
throwaway route when the surface genuinely has nowhere to live, and name it so it is obviously a
prototype.

**The default renders exactly what exists today.** With no `?variant=` param, the page is byte-for-byte
what it was before you touched it:

```tsx
// Next.js App Router; adapt to the framework in use
const variant = searchParams.get("variant");

return (
  <>
    {variant === "a" && <VariantA {...data} />}
    {variant === "b" && <VariantB {...data} />}
    {variant === "c" && <VariantC {...data} />}
    {!variant && <ExistingSettingsPage {...data} />}
    <PrototypeSwitcher variants={["a", "b", "c"]} current={variant} />
  </>
);
```

That default is what keeps this additive rather than destructive: the current design stays live and
untouched, the variants sit beside it, and a stray merge changes nothing users see.

### The switcher

A small fixed bar at the bottom centre, visually distinct from the page (high-contrast pill, subtle
shadow) so nobody mistakes it for part of the design being judged:

- **Left and right arrows** cycle variants, wrapping around, including the untouched default.
- **A label** showing the current key and its name: `B (sidebar layout)`.
- **Arrow keys** cycle too, except while an `<input>`, `<textarea>`, or `[contenteditable]` has focus.
- Clicking updates the URL through the router (`router.replace` on Next) so a variant is shareable
  and survives a reload.
- **Gated out of production**: wrap in `process.env.NODE_ENV !== "production"` so a stray merge cannot
  ship the bar.

Keep it in one shared component both shapes reuse.

## Make them actually different

Default to **3 variants**, capped at 5, past which they stop being distinct and become noise.

Variants must differ **structurally**: different layout, different information hierarchy, different
primary affordance. Three lightly-tweaked card grids is wallpaper, not a prototype. Where two drafts
come out similar, redo one with an explicit constraint ("this one uses no card grid").

Write down the plan in one line before starting:

> "Three variants of the settings page, switchable via `?variant=`, on the existing `/settings` route."

## Hand over and stop

Report the URL and the variant keys, then wait. The most useful answer is usually *"the header from B
with the sidebar from C"*, which is the design they actually wanted and could not have described up
front.

## Anti-patterns

- **Variants differing only in colour or copy.** That is a tweak. Real variants disagree about
  structure.
- **Sharing a layout between variants.** A shared `<Header>` is fine; a shared `<Layout>` defeats the
  point, because each variant must be free to throw the layout out.
- **Wiring a variant to real mutations.** Read-only is fine; point at a stub where a variant needs to
  write. The question is what it should look like, not whether the backend works.
- **Promoting variant code straight to production.** It was written without tests or error handling.
  Rewrite it when folding it in.
