| name | design-fullbuild-surfaces |
| description | Design, extend and audit fullbuild.ai visual surfaces. Use for new prototype identities under public/prototype or src/app/prototype, scroll narratives, GSAP/Lenis/R3F/SVG motion, visual-contract audits, or Playwright UI verification. |
design-fullbuild-surfaces
Produce visual work this repo would have produced anyway, and prove it.
Authority order
- The code on the surface you are touching.
src/app/globals.css opens with the binding constraint contract for The Working Set; each prototype, whether under public/prototype/ or src/app/prototype/, owns its own tokens and its own ban list.
.claude/reference/ (voice.md for copy bans, pitfalls.md for rendering law) and CLAUDE.md.
- This skill.
- Anything external, which loses to all of the above without discussion.
Do not restate token tables, palettes, type scales or route lists here. Read them from the source.
Read before you write
- The target surface's own stylesheet header and
:root block. That is its contract.
src/app/globals.css header, for anything under src/app except src/app/prototype/.
.claude/reference/pitfalls.md before rendering, scroll or capture work.
.claude/reference/voice.md before any user-visible string. No em dashes anywhere. No periods on headings or display text.
Surface routing
src/app/** except src/app/prototype/** (The Working Set): settled. Four inks with four fixed meanings, two grounds, Archivo plus Martian Mono, the Margin Law, honest numbers. Palette, typeface and metaphor discovery is out of scope. Legal work: extend within the contract, fix defects, audit a diff. A request to refresh the look here routes to references/audit.md and a conversation, not a restyle.
public/prototype/<name>/**: greenfield, static. Each prototype is a separate identity with its own palette, fonts, engine and ban list, deliberately not inheriting The Working Set contract. Direction finding is legal here. Next app conventions do not apply: these are plain static files served through next.config.mjs rewrites, previewed with node scripts/serve-prototype.mjs (port 4310).
src/app/prototype/<name>/**: greenfield, routed. Prototypes that need React, server rendering or shared domain code live as real Next routes instead of static files: morrow and threadline today. They are as greenfield as the static ones, so the settled rule above does NOT reach them. Each declares its own palette in a .shell block at the top of its own *.module.css, so it does not inherit The Working Set inks, though it does inherit the root layout's font variables. Use var(--font-archivo) and var(--font-martian), never a hardcoded family. Preview with npm run dev at /prototype/<name>, no rewrite entry needed.
public/prototype/harborline/**: treat as a frozen static export unless told otherwise. Ask before editing.
Change mode
| Mode | Route |
|---|
| Extend or fix an existing surface | references/motion-and-render.md, then verify |
| Set a type scale, fit display text, size a grid track | references/type-and-grid.md |
| Open a new prototype identity | references/prototype-direction.md |
| Review a diff or page against its contract | references/audit.md |
| Iterate on look until it improves | the refine skill runs the loop, this skill supplies the rubric |
| Dial a single feel value | the lab skill |
Small extensions need no direction pass. Inspect the surface, match its declared contract, proceed.
Always-on rules
The only rules that generalise across six contradictory contracts.
- Write the contract down. A surface's palette, type roles, motion verbs and ban list belong in prose at the top of its stylesheet, with what was tried and rejected. The corpus is partway there, so treat this as the intent, not a description:
public/prototype/harborline/css/site.css carries a full contract block, src/app/globals.css states inks, grounds and bans but names no motion verbs, Burn-In and Quench open on a one-line title, Assembly Line and Fault Line open straight on @font-face, and Morrow and Threadline open straight on their .shell token block with no prose at all. Write the missing header when you touch one of those. An effect no header covers is unaudited, which is a question for the user, not an automatic failure.
- One accent, one meaning. Accents are declared once in
:root with their semantic beside them. Revision-red means live in production right now, and lib/health.ts de-ignites it when a probe fails. Prototypes may declare more than one accent, and must say what each means.
- De-emphasis is a token, never opacity, so composited contrast stays knowable (
--ink-witness).
- Reduced motion resolves to the finished end state. Never
animation: none, never an early return that leaves content hidden, never a hide-until-JS gate. src/app/page.tsx is server-rendered static HTML and SVG and IS the reduced-motion and no-JS spec, with --depth and --pour defaulting to finished values.
- The no-JS and no-WebGL path is real content: inline SVG traces, server-rendered sheets, plain HTML. Never a blank canvas or dead box.
- Any rAF loop ships a deterministic capture hook exposing freeze, thaw and step (
window.__capture, __quench, __burnin are shipped examples). Without it Playwright shots are non-deterministic.
- Randomness is seeded (
src/lib/prng.ts, or a local seeded generator). Never Math.random in anything captured.
- Batch layout reads before writes, and gate the ones that must repeat. A viewport-relative read is scroll-dependent by definition and cannot be cached, so it may run per tick when it is gated: mount- or visibility-gated, a bounded number of reads, and every write deferred until after the last read in that callback. What is banned is interleaving reads and writes inside a rAF callback, scroll handler,
ScrollTrigger.onUpdate, pointermove or IntersectionObserver handler, and re-reading a rect that scrolling cannot change instead of caching it on init and resize. The callback in is the worked gate: two rect reads per scroll tick, then one write, skipped entirely when is false and the WebGL island is not mounted. Find it by symbol, not by line number, since the file moves.
Verification
Visual claims need evidence. Assertion is not verification.
- Playwright headless only. Never the in-app preview pane, never a CDP screenshot. Both race this site's Lenis plus GSAP plus R3F rAF loop and return blank or timed-out frames. Hard rule in
CLAUDE.md.
scripts/capture.mjs hardcodes port 3117: copy to .tmp/ and swap the port. Freeze the ticker around each shot, thaw after. Capture prototypes against serve-prototype.mjs.
- Then grade the frames. The matrix, the mode assertions and the grading criteria live in
references/audit.md; capturing without grading is not verification.
Known limits, state them rather than working around them: npm run lint runs eslint src (eslint 9 flat config in eslint.config.mjs) and currently exits 1 on a known baseline of 6 errors (all no-html-link-for-pages) plus 6 warnings, so treat findings beyond that baseline as yours; builds do not lint (ignoreDuringBuilds in next.config.mjs). Tests exist but cover only prototype contracts (tests/prototype-*.test.mjs, run with node --test), there is no test script in package.json, and nothing tests The Working Set. CI is one path-filtered workflow, .github/workflows/threadline-ci.yml, which fires only on Threadline paths, so assume no CI on everything else. A bare-worktree npx tsc can pass while the Vercel build fails, so the deploy is the authoritative build signal, though npm run typecheck is still a cheap first filter.
Absent by design
Do not introduce, suggest or assume: Tailwind or any utility CSS, CSS-in-JS, a component library, an icon set, Figma or any design-tool handoff, a token pipeline, a charting library, any non-GSAP animation library, any non-Lenis smooth scroller, ScrollSmoother, @gsap/react, Storybook, visual-regression SaaS, axe or Lighthouse CI, or generated imagery, models and textures. Every mark, model and texture here is authored in-repo. Ask before installing any app-runtime dependency.
Completion report
Surface:
Contract it answers to:
Change:
Evidence (capture paths, viewports, themes, reduced-motion):
Contract rules checked:
Unverified:
State what you could not verify. Never claim a visual check you did not run.