The master prompt
I want my AI to build pages on this stack without breaking anything.
The rules an AI must follow to produce correct, secure, on-brand pages here — tokens, the page pattern, the data rules, and the upgrade path. Paste it with the repo and your assistant inherits every guardrail.
Typical effort: Paste once, applies forever
The recipe — paste this to your AI
# Master prompt — build on the Genesis frontend-template with your AI
Paste this into your AI assistant (Claude, Cursor, etc.) together with this repository. It
teaches your AI the few rules that make a page correct, secure, and on-brand on this template.
Following it, your AI produces pages that match the rest of the site and inherit the platform's
guardrails without you having to know them.
---
You are building a website on the **Genesis frontend-template** — a Next.js (App Router) app
that talks to the Genesis API. Follow these rules. All paths below are relative to the app
root — the folder holding `package.json` and `site.config.ts` (the repo root in a standalone
clone; `apps/web/` inside the Genesis monorepo). The single best reference is
**`docs/conventions.md`** — the house style that *names* every pattern (API
namespaces, data hooks, forms, money, tokens, the component kit) and says which CI gate
enforces it; read it first. Other docs in `docs/`: `quickstart.md` (get it running),
`api.md` (how the Genesis API works — envelope, auth, errors, the domain catalog), `design.md`
(layout/type/spacing — how to make a page look right), `skinning.md`, `themes.md`, `extend.md`,
`modify.md`, `upgrade.md`, `features.md`. When unsure, copy an existing page in `app/`.
## Rule 0 — the upgrade path (check file ownership BEFORE editing anything)
This clone stays upgradeable only if you edit the right files. Every code/config file starts
with a `@genesis-managed:` marker: **`LANE`** files are yours (edit freely — `site.config.ts`,
`lib/content/*`, `public/**`, `components/**` outside `components/ui/`, new `app/` segments you
add). **`CORE`** files (the kit `components/ui/**`, `lib/**` outside content lanes,
`middleware.ts`, the gates) are hash-locked in `template.lock.json` — editing one reds
`npm run upgrade:check` (also inside `pnpm verify` and CI). **`FRAMEWORK`** pages take their
words from the lanes, so change the lane, not the page. If a change seems to need a CORE edit,
it almost never does — see `docs/modify.md` for the lane override pattern, and `CLAUDE.md` for
the full lane map. `DEPLOY.md` (repo root) is the canonical clone→live walkthrough.
## The rules (non-negotiable — they keep pages consistent, skinnable, and secure)
**The one principle behind these:** the front end is a *view, never a source of truth* — it
hardcodes **no canonical value**. Colors resolve from semantic tokens; money, status/lifecycle
vocabulary, and business rules come from the API. Rules 1 and 4 below are the two faces of this
single discipline — a hardcoded hex (`bg-blue-600`) and a hardcoded domain value (`['completed',
'cancelled']`) fail the same way: the page silently drifts and breaks the moment the brand or the
backend vocabulary changes. When the right source isn't wired yet, mark the spot a **STOPGAP** —
never present a hardcoded copy as the canonical pattern (it's the example the next AI will copy).
1. **Colors come from semantic tokens, never hardcoded.** Use `bg-surface-card`,
`text-content-primary`, `text-content-secondary`, `text-content-muted`, `bg-action-primary`
(+ `hover:bg-action-hover`), `border-surface-border`, `text-content-link`. **Never** write
`bg-white`, `text-gray-900`, `bg-blue-600`, or `dark:` variants — those break re-skinning
and dark mode. The 4 proven color schemes (light/dark/high-contrast/outdoor — each contrast-checked by `pnpm verify`) plus the `system` follow-the-OS setting are handled by
CSS variables automatically. Re-skin a tenant by editing `--brand-*` in
`app/globals.css`/`site.config.ts` — never by editing pages.
2. **Reuse the component kit; don't hand-roll.** Import from `@/components/ui`: `Button`,
`Card`, `Input`, `Textarea`, `Select`, `Field`, `Label`, `Switch`, `Tabs`, `Alert`, `Badge`,
`StatusBadge`, `Modal`, `Table`, `MoneyDisplay`, `PageHeader`, `Loading`, `ErrorState`,
`EmptyState`, `Spinner`. See them live at `/components`. **Money always renders via
`MoneyDisplay`; status always via `StatusBadge`.**
3. **Follow the canonical page pattern** for any data-driven page — *fetch → states → gate →
CTA*:
```tsx
'use client';
import { useApiQuery } from '@/lib/use-api';
import { someApi } from '@/lib/api/<domain>';
import { Can } from '@/lib/permissions';
import { PageHeader, Button, Loading, ErrorState, EmptyState } from '@/components/ui';
export default function Page() {
// Name the query <entity>Query and the data <entity>/<entities> — not q/o (conventions §3).
const itemsQuery = useApiQuery(['<domain>', '<entity>'], () => someApi.list()); // 1. FETCH (typed client; decodes the envelope)
if (itemsQuery.isLoading) return <Loading />; // 2. STATES
if (itemsQuery.error) return <ErrorState error={itemsQuery.error} onRetry={itemsQuery.refetch} />;
const items = itemsQuery.data?.items ?? [];
if (items.length === 0) return <EmptyState title="Nothing yet" description="..." />;
return (
<>
<PageHeader title="..." description="..."
actions={<Can permission="<perm>"><Button>Primary action</Button></Can>} /> {/* 3. GATE + 4. CTA */}
{/* ...render items with @/components/ui... */}
</>
);
}
```
*Read-only page (no create/write action)?* Same pattern — just gate the **read CTAs or
downstream-navigation links** (e.g. a "View details" link) with `<Can permission="...">`
instead of a create button. The gate goes on whatever the user can do from the page; if the
page has no gated actions at all, it's fine to render it ungated (the API still enforces the
data).
*Building a form that **submits** data?* Use the one form pattern (conventions §4):
**react-hook-form + Zod** via `@/lib/form` with the kit `<Input>`/`<Field>`/`<FormError>`.
The Zod schema is **UX validation only** — the API re-validates authoritatively (the wall),
so never put a business rule in the schema. A lone control that only filters/sorts the current
view (a search box, a toggle) is **not** a form — use the kit control directly. Copy
`app/login/page.tsx` or `app/(dashboard)/patterns/page.tsx`.
4. **Security is inherited — don't undo it.** Gate UI a user can't use with `<Can permission="...">`
(operator app) or `useMember().can('...')` (member portal) — this hides it; the API still
enforces access, so gating is never the only lock. Talk to the backend **only** through the
typed client in `@/lib/api/*` (it decodes the universal `{success,data,error}` envelope —
never raw `fetch`). **No business rules or money math in the page** — the API computes, the
page displays. **Never hardcode domain status/lifecycle values (e.g. an event-status list)
in the page to filter or categorize** — that vocabulary lives in the backend and an FE copy
silently drifts when it changes; filter on an API-provided signal/flag instead. If the API
doesn't expose the signal yet, mark the spot a STOPGAP and don't present it as canonical.
Money values are **strings**, never floats. Don't expose raw database ids.
5. **Mobile-first + accessible.** Design for a phone first (≤375px); 44px minimum tap targets;
real `<label>`s, heading order, alt text. Use **logical** direction utilities (`ms`/`me`,
`ps`/`pe`, `text-start`/`text-end`, `border-s`/`border-e`, `start`/`end`) instead of physical
ones (`ml`/`mr`, `pl`/`pr`, `text-left`/`text-right`, `left`/`right`) so pages mirror correctly
for right-to-left languages.
6. **Config-driven.** One file — `site.config.ts` — controls brand tokens, analytics IDs, and
which modules are on/off and in what order. To make a page toggleable, add it to the module
catalog (`components/shell/nav-config.tsx`) with a stable `key`.
7. **Docs-as-code.** A feature isn't done until it has an entry in `docs/features.json` +
`docs/features.md` (what it is, how to enable it, the API behind it, the page). Add the entry
as part of building the feature.
## Three ways to change the template (pick the lightest that does the job)
- **Skin** — change `--brand-*` tokens only. Painless; auto-upgrades when the template updates.
- **Extend** — add new pages/modules via the canonical pattern. Additive and safe; never edits
core files, so updates still flow.
- **Modify** — edit a component in `components/ui/`. Powerful, but you then **own** that piece
and merge future template improvements to it by hand. Modify only when skin/extend won't do.
## Common tasks
- **Add a page:** create `app/(dashboard)/<route>/page.tsx` (or a public route), copy the
canonical pattern, style with tokens + `@/components/ui`, gate the actions, add a nav entry +
a `docs/features.*` entry.
- **Re-brand:** edit `--brand-*` in `app/globals.css` / `site.config.ts`. Done.
- **Turn a feature on/off:** edit `site.config.ts` (`modules`, `marketing.nav`).
- **Add analytics:** put your GA/GTM/Pixel ID in `site.config.ts` `analytics`. It works on every
page; the CSP opens only for what you set.
## Prove it before you ship — `pnpm verify`
After you build, run **`pnpm verify`**. It fails loud on a hardcoded color, hand-rolled money
(not `MoneyDisplay`/`formatMoney`), a raw `fetch()` instead of the typed client, a raw form
control instead of the kit, hardcoded domain vocabulary, a sub-AA theme, or a broken behavior —
so you can't ship a regression by accident. Green `pnpm verify` = your page honors the contract.
Build the smallest correct thing, copy the nearest existing page, and keep to the rules above.
Source of truth
This recipe ships inside the template as docs/prompts/master-prompt.md — this page renders that exact file. The repository is always the newest version: https://github.com/srisenmay/bringthefront-template. Clone the template and you get every recipe locally, versioned with the release you are on.