← Files HiggsfieldARCHIVED FILE
skills/website-builder/references/website-flow.md
31.4 KB · Oct 2, 2026 · 00:02 UTC
# Website flow — `type: "website"` (independent brand, phased pipeline)
> **Use `sandbox_exec` for all code edits.** Read `references/repo-and-sandbox.md`
> first: `website_repo_access` checks out and pushes without exposing credentials.
> Commit in the returned checkout path and push before the 15-minute lease expires.
You are building ONE per-website Cloudflare Worker: a **React 19 + TanStack
Start** app that is **server-rendered (SSR)** and deploys as a single Worker
served at the website's own subdomain. A `type: "website"` product is a
STANDALONE brand — no Higgsfield integration, no Quanta, no fnf SDK; the
user's brand is the only brand on the page. If mid-build the request turns
out to need Higgsfield generation, sign-in, or credits, that is a
`type: "app"` — switch to `references/app-flow.md`.
**Higgsfield as the asset engine — EVERY build.** All visual assets on every
website are generated with the Higgsfield tools per
`references/asset-system.md`. (Internal use of Higgsfield generation tools is
invisible to visitors and always fine.)
**Scope: build the real, full app.** This stack ships complete, production-grade
applications: real frontend AND backend, database (D1), file storage (R2), auth,
third-party API integrations, background work. When the user describes an app,
tool, site, or web product, BUILD IT with the website builder — it is the default
for any web target. Never ask the user to confirm the platform, never downgrade to
a "demo"/mockup as the safe option, and don't stall on scope questions beyond the
single intake call defined below. Take the fullest reasonable interpretation
and ship a working live site.
**Pick the template at create time.** `create_website` takes an optional
`template` for websites, and it decides what code the repo arrives with:
| Animation mode | Call | Repo arrives with |
|---|---|---|
| `animated-website` (default) | `create_website(type="website", template="scroll-scrub", …)` | The scroll-scrub engine pre-built — you fill in scenes, never write a controller |
| `non-animated` | `create_website(type="website", …)` — omit `template` | The plain scaffold |
Never pass an app template (`studio`, `preset`, `app-detail`, `custom`) for a
website — it is rejected. Resolve the Animation mode at intake FIRST, because it
picks the template, and the template is chosen once at create.
**Repo layout.** The website project lives in **`app/`** — its own `package.json`,
`src/`, `packages/`, `migrations/`, build config, and the deploy inputs
(`app.manifest.json`, `wrangler.jsonc`). Run every `bun`/build command from there.
---
## THE PIPELINE — phases in order, artifacts + gates, no skipping
Every NEW build runs this machine. Each phase produces a named artifact the next
phase consumes. Do not reorder, merge, or skip phases — "simple" briefs are where
generic output happens. (Follow-up edits to an existing site do NOT restart the
pipeline — see "Small edits" at the bottom.)
### Before Phase 0 — intake (ONE `ask_user_question` call, then never again)
The `type` is already resolved (the main skill also asks, in that first message,
whether to publish to the community feed when ready — remember that answer for
Phase 6). This single intake call carries the two website-specific questions
worth asking; put both in the ONE `ask_user_question` call and never ask a second
round.
1. **Animation — MANDATORY, ALWAYS ask on every `type: "website"` build.** This
question is NEVER skipped — ask it even when the request seems to imply a
choice ("an animated site", "a plain static page"), even for a "simple" or
"quick" site, even when you think you already know the answer. The user must
make this call, not you. Offer exactly two options:
- **Animated (recommended)** → sets `Animation mode: animated-website` — the
scroll-scrub camera journey (the product default; mark it Recommended).
- **Non-animated** → sets `Animation mode: non-animated` — a well-crafted site
with lighter/optional motion, no mandatory camera journey.
If the request already leans one way, still ask — just point the Recommended
marker at the implied option. Record the picked value on the brief's
`Animation mode` line (Phase 0). ONLY if the user is genuinely unreachable /
never answers do you proceed on the default (**Animated**) and say so in one
line — never as a shortcut to avoid asking.
2. **Brand constraints** — an existing brand to honor (ask for
colors/fonts/logo/photos/links) vs. free rein ("design the brand for me").
Whatever they don't have, you generate: the full identity kit plus the
personalization ladder in `references/asset-system.md` (logo family, icon set,
patterns, illustrations, state artwork, product universe). Free rein is the
richer path, not the degraded one.
The Animation question (1) is always asked — never skip the call to avoid it. You
may drop only question 2 when the brief already answers the brand question. If the
user skips or is unreachable, default to **Animated** and say so in one line.
Never ask a second round.
### Phase 0 — Concept (`app/design-brief.md`, committed, BEFORE any code)
Write the brief (~40 lines). Every section mandatory; a generic line ("modern and
clean", "Inter", "blue accent") means the brief is not done:
- **Design read** — one sentence: who is this for, what emotional register.
- **Concept spine** — a nameable narrative idea threading the whole page (e.g.
"the site is a calibration instrument", "an archive dossier", "a stage").
Pick from `references/reference-boards.md`'s spine list or invent better.
- **Delivery tier** — `cinema` (**default**: Lenis+GSAP, Tier-1 hero, scroll
chapters — carries the animated website) · `spectacle` (briefs saying
awwwards/webgl/3d/immersive: cinema + WebGL/3D/scrub + custom cursor + a
second beat) · `editorial` (calm/minimal/B2B: typography + imagery + bespoke
chrome, micro-motion only). The animated website is the default Tier-1
experience regardless of tier, so cinema/spectacle is the normal home for it.
`editorial` drops the scroll-scrub journey to micro-motion — treat choosing it
as one of the "user explicitly asked for something other than an animated
website" cases, not a default an ordinary B2B/minimal brief falls into on its
own.
- **Locked palette** — exact hexes + a one-line defense. Hard bans (mechanical,
gate-checked): (1) graphite/near-black + orange/amber/ember accent, (2)
near-black + neon cyan/blue/green accent, (3) beige/cream + brass/clay/
oxblood, (4) AI purple/violet glow, (5) the palette family of your previous
build in this chat. Overridable only by the user's explicit brand colors.
See `references/reference-boards.md` for what to reach for instead.
- **Locked type** — pairing from the recipe's tables; serif only with a written
brand justification.
- **`Animation mode` — MANDATORY explicit state, set from the intake answer.
HARD STOP.** The brief MUST contain a literal line `Animation mode: <value>`
(the value the user picked at intake — see "Before Phase 0"; default Animated
if they skipped) and you may not leave Phase 0 without it. Only two values are
legal:
- **`animated-website`** (the recommended default — from "Animated" at intake):
the scroll-scrub journey (**A4**), where the visitor's scroll plays a
generated film. Built from the `scroll-scrub` template, so the engine is
already in the repo. This value OBLIGATES you to also write the journey
block into the brief NOW, before boards or generation: read
**`references/scroll-scrub.md`** and add its **Journey shape**
(`single-shot` — the default — or `multi-leg`), **Journey** (the chapters),
**world grammar**, and **mobile framing**, plus one sentence on how the
journey enacts the concept spine. `Animation mode: animated-website` with no journey
block is an INCOMPLETE brief — a hard stop, not a proceed-anyway. On this
value, do NOT shop the wow-catalog — the animated website IS the technique;
the anti-convergence "no repeat" rule does NOT force you off it (differ on the
OTHER five axes instead: world/subject, journey shape, palette, type, CTA
garments, corner language).
- **`non-animated`** (from "Non-animated" at intake, or a request that clearly
asked for it): a well-crafted site with lighter/optional motion and NO
mandatory camera journey. Legal ONLY as the user's own choice — append the
reason on the line, e.g. `non-animated — user picked Non-animated at intake`
or `non-animated — "<verbatim request>"`. Your own taste, "calm/minimal/B2B",
or "it's just a simple landing page" is NOT a reason to pick this — when in
doubt it is `animated-website`. On this value the site still clears the
`wow-maker.md` craft floor (bespoke assets, motivated micro-motion, real
typography); reach into **`references/wow-catalog.md`** for a lighter Tier-1
technique (named with its catalog ID) or run the editorial tier. Never ship a
dead flat page.
Either way, a passive autoplay loop is never the Tier-1 mechanic.
- **Section plan** — ordered, one layout family per section, no consecutive
repeats, ≥4 families for 6+ sections, eyebrow budget ceil(sections/3).
- **Asset plan** — the full kit per `references/asset-system.md` (hero visual,
section plates, content imagery, custom icon set, logo/monogram, OG; + video
loop for cinema, + GLB for spectacle).
- **CTA inventory** — every CTA named with its OWN interaction identity (no
shared button style — see bespoke-chrome in `references/image-to-code.md`).
The brief is a contract: later phases may not silently contradict it — edit the
brief first and say why.
### Phase 1 — Reference boards (design the page as IMAGES)
Read **`references/reference-boards.md`** and execute it: ONE horizontal design
reference image PER SECTION via `generate_image`, one committed
combinatorial pick (theme paradigm, background character, typography character,
hero architecture, section system, 4 signature components, narrative spine,
second-read moment), composition anchor VARYING per board, palette locked across
all boards. **Look at every board** and re-roll any that reads template-y
(budget 2 re-rolls). Boards land in `refs/` in the repo. The boards ARE the
design — do not start Phase 3 with a generic board in the set.
### Phase 2 — Asset system (submit everything, then build while it renders)
Read **`references/asset-system.md`** and submit the ENTIRE kit as async jobs
right after the boards are chosen: hero visual (2 candidates + interaction
pair), section plates, all content imagery, the custom generated icon set, the
logo/monogram + favicon, the OG card — plus video loop (cinema) / GLB
(spectacle). The launch cover/OG is NOT part of this batch — it has ONE start
point, in the same beat as the film (`references/scroll-scrub.md`), because
starting it before the brand identity is settled is how a build ends up
rendering the cover twice and throwing the first one away. Start it once, never
restart it just because `app-meta.json` still looks empty — finish the run you
already have. Poll between build steps; download into `app/public/assets/`;
verify kit coherence when it lands (re-generate anything whose grade fights the
boards). Never idle waiting on renders; never fall back to stock/picsum/CSS-only.
For the animated website (A4, the default), follow `references/scroll-scrub.md`:
`single-shot` (the default journey shape) is ONE video call with nothing to
sequence, so submit it alongside the rest of the kit. Only `multi-leg` has real
rendered-frame dependencies, and there the exact-frame forward legs are
intentionally sequential — the "submit everything up front" rule never overrides
a real frame dependency, and nothing else justifies serializing.
### Phase 3 — Build to the boards, section by section
Turn discipline (see SKILL.md "Turn economy"): plan the section, then write its
file ONCE and complete — no write-then-patch loops, no re-reading files you just
wrote, and batch independent edits in a single pass. This phase is where builds
burn the most turns.
Read **`references/image-to-code.md`** and follow its discipline per section:
re-read the board at build time, extract text/type-scale/spacing/color/
component logic, implement faithfully, anti-drift (when your habit disagrees
with the board, the board wins). The craft floor in
**`references/design-recipe.md`** still applies everywhere (hero discipline,
layout bans, copy rules, zero em-dashes). Bespoke chrome: every CTA designed in
its own component with its own interaction identity; no site-wide button
utility classes. Registry components (`references/wow-maker.md` §5) remain
available as raw material — restyled to the boards, never default-skinned.
Build static-but-complete; motion is the next phase.
**HARD STOP for `Animation mode: animated-website` — build the journey as the
spine, not an afterthought.** The scroll-scrub engine is ALREADY in the repo
(the `scroll-scrub` template) — do not rewrite, re-paste, or re-derive it from
the bundled asset files. Fill in `app/src/scroll-scrub-scenes.ts` and compose
`app/src/routes/index.tsx` around `<ScrollScrub />` in THIS phase: the semantic
chapters are the page structure, not decoration added later. Do NOT build a
generic static page of sections and plan to "add the journey later" — that is
the exact failure this flow guards against, and it is caught by the Phase 5
gate. If the film is still rendering, build the page against its posters and
swap the clip in when it lands — never substitute a plain static layout for the
animated website.
### Phase 4 — Motion pass (tier-mandated, one focused pass)
- **cinema/spectacle:** Lenis smooth scroll bridged to GSAP ScrollTrigger
(`autoRaf: false` + `gsap.ticker` — without the bridge, scrub stutters).
- The **Tier-1 hero mechanic** from the brief, fully executed — a half-wired
version fails review. The hero is the wow carrier and it must respond to
the USER'S INPUT (scroll plays the movie), never a passive autoplay loop.
Passive motion the user can't influence does not count as the Tier-1 mechanic.
- **Animated website (default):** the scroll-scrub film from
`references/scroll-scrub.md` IS the hero mechanic — it replaces the ordinary
single hero frame sequence. Let its controller own scroll-to-video time; keep
the Lenis/GSAP bridge for surrounding motion, and never drive the same media
with a second ScrollTrigger timeline. Only when the user explicitly asked for
a non-animated-website treatment do you instead wire the chosen catalog
technique — e.g. the single scroll-scrubbed hero film per `asset-system.md` §7.
- Scroll-chapter reveals: staggered headline builds (`split-type` + GSAP or
registry text components), per-section distinct timing; work rows / cards
with hover reveals; magnetic nav/CTA physics via `useMotionValue`, never
`useState`.
- **Screenshot-safe reveals (hard rule):** nothing waits at `opacity: 0` for an
IntersectionObserver. The safe recipe: headline/text builds fire ON MOUNT
(not viewport-gated); scroll-linked effects animate transform/scale/clip
ONLY, never opacity-to-zero; hover states may use opacity freely. Ignore
any `whileInView` fade-in examples in the ingredient libraries — they fail
this gate. A full-page headless screenshot must show every section.
- **Pin-spacer trap:** a GSAP pinned hero injects a spacer that reads as a
large blank band in full-page screenshots (guaranteed review failure).
Use `pinSpacing: false` with the following content sliding over the pinned
layer, or otherwise verify the full-page shot has no dead band after the
hero.
- EVERYTHING `prefers-reduced-motion`-gated with static fallbacks; `[C]`/`[W]`
components behind the SSR pattern (wow-maker §6). A top-level `window`
reference crashes SSR — the #1 recurring build failure.
- spectacle only: custom cursor + WebGL/3D/scrub second beat.
### Phase 5 — Mechanical gate (before first deploy; every item fixed)
Run the grep checklist in
**`references/review-rubric.md` §A**: placeholders; em/en-dashes; banned palette
families in tokens; eyebrow ration; unreferenced generated assets (every kit
file used); `h-screen`; SSR safety; reduced-motion coverage; **repeated CTA
classes** (bespoke-chrome violation); **opacity-0 + whileInView** combinations;
section plan honored; copy self-audit. This is a completion gate — do not
deploy with a failing item.
### Phase 6 — Deploy
0. `bun run typecheck` once, from `app/` — ~15s locally vs a failed deploy
(the platform build runs the same `tsc`, but a deploy round-trip plus the
fix-and-redeploy loop costs minutes and several turns). Fix what it finds,
then deploy. This is the ONE pre-deploy local check; do not also run
`bun run build` unless typecheck passed and the deploy still failed.
1. `deploy_website` — the site is live at its URL immediately.
2. Report: the live URL ("Your site is live: <url>") + one-line concept
statement + anything honestly skipped. Speak in product terms — no
repo/commit/deploy jargon (see the SKILL.md "Talking to the user" rule).
Do NOT navigate to, screenshot, or run image analysis on the deployed site —
the mechanical gate (the grep checklist in `references/review-rubric.md` §A) is
the only verification.
3. **Publish.** If the user opted in at intake, publish automatically now that
the site is deployed with its cover + metadata filled — call
`publish_website` (don't wait to be asked) and share the marketplace listing
URL it returns. If they didn't opt in, publish only when they ask. (The
$100k contest is for `type: "app"` products — don't pitch it for a plain
website.)
---
## Design references — read order
## Design references — read order
1. **`references/design-recipe.md`** — craft floor (ALWAYS read; short).
2. **`references/scroll-scrub.md`** — the **animated website**, which is the
DEFAULT Tier-1 experience for every website: read it in Phase 0. It owns the
specialized boards/assets/runtime sequence and bundled Markdown code
references for that build. Only read **`references/wow-catalog.md`** (Tier-1
technique menu + anti-convergence ledger + Phase 4 implementation contracts)
when the user explicitly asked for something other than an animated website.
3. **`references/reference-boards.md`** — Phase 1: per-section design boards.
4. **`references/asset-system.md`** — Phase 2: the Higgsfield asset kit.
5. **`references/image-to-code.md`** — Phase 3: faithful implementation +
bespoke chrome + the CTA garment catalog.
6. **`references/review-rubric.md`** — Phase 5: the mechanical gate.
7. `references/wow-maker.md` — ingredient directory: motion/3D libs (§4),
component registries (§5), signature effect patterns (§2), SSR pattern (§6).
Only listed free/permissive sources may be used.
8. `references/design-taste-frontend.md` — the full deep-dive playbook behind
the recipe; consult for specific situations, not required start-to-end.
9. `references/app-cover.md` — the branded 3:2 launch cover + OG image
(stadium-capsule mask via the inlined compose script; hosted style refs).
REQUIRED before every publish (`og_image_url` + `marketplace_cover_url` are
mandatory feed-card fields — never call `publish_website` while they are
empty), and whenever the user asks for a cover/OG image directly.
Do NOT search the skill library for other design guidance — everything is here.
Then route to the FUNCTIONAL reference for the task:
| Task | Read |
|---|---|
| **Any website (the DEFAULT — animated website)** / scrollable world / continuous camera journey / diorama fly-through / browse-through-an-industry site | `references/scroll-scrub.md` — the `scroll-scrub` template, journey shapes, film pipeline + mobile/QA contract |
| TanStack Start routes, SSR, server functions, Cloudflare Worker runtime | `references/runtime-and-infra.md` |
| Cover / OG image ("cover", "обложка", "OG image", publish prep) | `references/app-cover.md` — branded 3:2 cover + capsule OG mask |
| SEO: meta tags, OG/Twitter cards, robots/sitemap, JSON-LD, entity, GEO, audit | `references/seo.md` |
| Security: Worker hardening, OWASP audit, threat modeling | `references/security.md` |
## Stack
- **TanStack Start** (file-based routing under `app/src/routes/`, SSR via
`app/src/server.ts` → a Worker `export default { fetch }`). No Next/Remix/Astro
conventions, no `app/src/pages`.
- **Vite 7 + bun**. Build emits `dist/server/server.js` (the Worker) +
`dist/client` (hashed static assets). Tailwind v4 is wired in `app/src/styles.css`
(it also imports Quanta's Tailwind entry for the template
bundle — leave that wiring alone even though websites use neither). Legacy
shadcn/ui files may exist from the scaffold. Websites use custom
Tailwind/CSS only — never import `@higgsfield/quanta/*`.
- **No separate Hono/Express backend.** Server logic is TanStack **server
functions** (`createServerFn`) and **server routes**. App-local API routes are
allowed when a platform contract requires them (for example a webhook
receiver or a JSON endpoint the site's own client fetches).
## Hard rules
### 0a. Vendored packages and template modules
The `app/packages/` directory contains managed snapshots vendored from the
upstream Higgsfield web app (`@higgsfield/fnf`, `@higgsfield/fnf-react`,
`@higgsfield/quanta`). Websites never use them, but do NOT edit or delete
them. Template-owned infrastructure lives in `app/src/module/**`.
### 0b. Supercomputer Design mode inspector
Generated websites support a Higgsfield design inspector bridge so
Supercomputer Design mode can edit the live site. The split is strict:
- The Higgsfield editor (parent window) owns the iframe UI, hover overlay,
edit popover, origin/session checks, and edit prompt submission.
- This template owns the child iframe runtime through
`app/src/module/design-inspector`.
- Agents never manually implement inspector code, refs, source markers, or
`data-hf-*` attributes.
Local scripts (the deploy build is CI-owned):
- `bun run build` is inspector-free by default: no inspector runtime and no
source metadata. Setting `HF_DESIGN_INSPECTOR=1` in the env turns the same
build into the inspector-enabled one (this is what platform CI does on
every deploy).
- `bun run dev:design` is local dev with the inspector enabled.
The platform CI builds every deploy with `HF_DESIGN_INSPECTOR=1`, so the live
deployed site always carries the design inspector and IS the surface
Supercomputer Design mode opens. Exact source metadata is attached with
callback refs and a `WeakMap`, not DOM attributes. Keep the guarded
dynamic install in `app/src/routes/__root.tsx` and the Vite integration in
`app/vite.config.ts` wired to `app/src/module/design-inspector/vite`.
There is ONE deploy per website: `deploy_website` (it takes only the
website_id — there is no env choice and no separate preview stage). Every
deploy ships the live public site at the website's URL immediately. Deploy
freely while iterating; only `publish_website` (the feed listing) is gated on
an explicit user ask. Never hand-edit the build script to toggle the
inspector: don't hard-code `HF_DESIGN_INSPECTOR=1` into `build` — the platform
CI sets the flag itself.
### 1. SSR-safe rendering
Every route renders on the server per request. NEVER touch browser-only globals
(`window`, `document`, `localStorage`, `navigator`) at module top level or during
render — only inside `useEffect`/event handlers, or guarded with
`typeof window !== "undefined"`. A top-level `window` reference crashes SSR.
### 2. Server-only code stays server-only
Put server logic in `createServerFn(...).handler(...)` or a `*.server.ts` module
(the `.server.ts` suffix keeps it out of the client bundle). Secrets and
bindings are read **server-side, per request** — never shipped to the browser.
### 3. No Higgsfield integration — but a REAL backend of the site's own
A `type: "website"` product never calls `https://fnf.internal/*`, never shows
"Sign in with Higgsfield", and never imports the fnf SDK. It still gets a real
backend wherever the product needs one: server functions (`createServerFn`),
app-local API routes, sessions, business logic, and real persistence (D1) —
never in-memory arrays, `localStorage`-as-database, or fixture data. In-app
auth for the site's OWN users (accounts, teams, dashboards) is built with the
website's own routes/storage. If the request needs generation or Higgsfield
accounts, it is a `type: "app"` — switch to `references/app-flow.md`.
### 4. Cloudflare bindings via `cloudflare:workers`
Any infra you opt into (D1 `DB`, R2 `STORAGE`, KV `KV`) is read server-side
through `app/src/lib/bindings.server.ts` (`import { env } from "cloudflare:workers"`).
Each binding is present ONLY if declared in `app/app.manifest.json`, so the typed
accessors are optional — guard before use. Do not thread `env` through React
props or read it at module top level.
### 5. Opted-in storage is LIVE — one deploy, one database
If you opt into D1, R2, or KV, each is a SINGLE instance behind the ONE live
deploy. There is no staging copy: any migration or data change hits **live
production data** directly.
- `env.HF_ENV` is always `"production"` on deployed builds; it CANNOT switch
the database/bucket.
- A destructive migration you run "just to test" runs against **live user
data**. Prefer additive migrations (`CREATE TABLE IF NOT EXISTS`, `ADD COLUMN`).
### 6. `app/app.manifest.json` declares infra — NOTHING is provisioned by default
A new website gets **no D1, no R2, no KV, no Durable Object**. Opt in only when
the website actually needs it:
- `"db": true` → a D1 database, bound `env.DB`
- `"r2": true` → an R2 bucket, bound `env.STORAGE`
- `"kv": true` → a KV namespace, bound `env.KV`
- `"durableObject": "ClassName"` → a Durable Object, bound `env.ROOMS`
- `"container": true` (or `{ "instanceType", "port", "sleepAfter" }`) → a Docker
container for heavy/long-running work, bound `env.CONTAINER` — see
`references/containers.md`
Counts are capped (≤1 each) by the platform, which PROVISIONS the resource and
binds it at deploy. The committed `app/wrangler.jsonc` is build/dev input only;
the platform OVERWRITES its `name` + bindings at deploy — declare infra in
`app/app.manifest.json`.
**KV is eventually consistent** (NOT Redis): config, feature flags, cached reads
— NOT counters, locks, or read-after-write. Use a Durable Object for strong
consistency.
For a **Durable Object** you must ALSO `export class ClassName extends
DurableObject {…}` from `app/src/server.ts` (alongside the default `{ fetch }`
export).
For a **container** — heavy or long-running work a Worker can't do: set
`"container"` in the manifest and follow **`references/containers.md`** (exact
Dockerfile, the platform-fixed `AppContainer` class, keep-alive + 3-hour-deadline
pattern, fnf via container token). Containers are **off by default**.
## Editing map
- Pages / routing → `app/src/routes/**` (file-based; `__root.tsx` is the shell).
- Server logic → `createServerFn` (see `app/src/lib/api/example.functions.ts`) or
`*.server.ts`.
- Bindings access → `app/src/lib/bindings.server.ts`.
- Infra declaration → `app/app.manifest.json`; `app/wrangler.jsonc` = build/dev input.
- Durable Object class → exported from `app/src/server.ts`.
- Components → custom components per the boards; app-local files in
`app/src/components/**`. Do not start from `app/src/components/ui/*` unless
migrating a legacy shadcn piece.
- Styles / theme → `app/src/styles.css` wires Tailwind v4. Websites: a custom
token layer from the design brief — no q-prefixed utilities, no site-wide
CTA utility classes.
- D1 schema → `app/migrations/000N_*.sql` (additive; see rule 5).
- Pipeline artifacts → `app/design-brief.md` (Phase 0) + `refs/*.png` (Phase 1);
commit both.
## Verify + deploy
The trusted platform CI builds the website on **every deploy** (with
`HF_DESIGN_INSPECTOR=1`, so the live site carries the design inspector), so a
deploy already gives you the authoritative type + build result. Do NOT
reflexively `bun install` + `bun run build` just to check your work. The
sandbox cannot deploy/migrate (no Cloudflare token); the trusted platform CI
does that.
**Default: run the pipeline, pass the Phase 5 gate, deploy**
(`deploy_website` — the site is live immediately). Never list the site on the
community feed unless the user explicitly asked to publish.
**Publishing ("show in feed").** When the user asks to publish / share / put the
site on the feed, call `publish_website` — it lists the site on the Higgsfield
community feed. **Publishing no longer deploys**: it lists whatever is already
live, so `deploy_website` FIRST — and after ANY later change, deploy again to
ship it (re-publishing does not re-deploy and won't pick up un-deployed
changes).
**HARD GATE — the cover is NOT optional. Calling `publish_website` while
`og_image_url` or `marketplace_cover_url` is empty is a BROKEN publish** (the
feed card renders ONLY from `app/src/app-meta.json`; an empty `og_title` makes
the listing INVISIBLE, an empty cover makes it a blank card). The publish
sequence is: (a) READ `app/src/app-meta.json`; (b) if `og_image_url` or
`marketplace_cover_url` is empty → STOP, read `references/app-cover.md` and
generate + upload the cover NOW — do not skip this because the user "only asked
to publish", the cover IS part of publishing; (c) fill ALL fields below with
real values (never placeholders); (d) commit + push; (e) `deploy_website` to
ship the pushed changes (publish no longer deploys — it lists what's already
live); (f) only then call `publish_website`:
1. `og_title` — the card's title (also the browser tab title).
2. `og_description` — the card's one-liner.
3. `og_image_url` — REQUIRED: the cover image, generated per
`references/app-cover.md` (the branded 3:2 cover + stadium-capsule OG
mask) if none exists yet; upload the OG file with `media_upload`
and set the returned URL.
4. `marketplace_cover_url` — REQUIRED: the plain (unmasked) cover, the same
generation's `<name>_cover.png` from `references/app-cover.md`, uploaded
with `media_upload`. One generation fills both this and `og_image_url`
— there is never a reason to have one without the other.
5. `favicon_url` — the card's logo/icon (generate one if none exists yet).
6. `og_video_url` — the **cover video**, OPTIONAL and permission-gated: OFFER
it to the user ("want a short cover video for the feed card?") and ASK
PERMISSION FIRST — generating a video costs credits; never generate it
unprompted. If they say yes, follow "Cover video" in `references/seo.md`.
(1–5 are generated without asking — they are part of the publish, not a
separate credit decision; only the cover VIDEO (6) needs permission.)
A plain `deploy_website` remains the way to ship the live site WITHOUT a feed
listing.
**Run the local checks only when you actually need them** — from `app/`:
```bash
cd app
bun install # only when you changed dependencies / package.json
bun run typecheck # tsc --noEmit — only to chase a type error on deploy
bun run build # local build — only to chase a build error on deploy
```
Run them when: you changed dependencies or build/runtime config, you're debugging
a build/type error, or a command genuinely needs `node_modules`.
**Small edits to an existing site** (copy tweak, one component, styling fix): the
pipeline does not restart. Make the edit, deploy.
**Before claiming a build done / deploying, no placeholders may remain** — no
`<...>`-style tokens, `lorem ipsum`, or scaffold blank-page markers
(`REMOVE_THIS` / `blank-app-v1`). This is covered by the mechanical gate (the
grep checklist in `references/review-rubric.md` §A).
SHA-256: 401b6b4cae102b66c59f5041869b67f3f4190d378ca8e88a4aec4568f57619f5