name: webflow-build description: Build a Webflow site in Webflow from its approved localhost prototype (built first from the Figma design, in Bench or by Claude), with the Webflow MCP and this wf-template repo: variables, tag styles, classes and CMS, the repo port and loader, the style guide page, the media page, components, then pages section by section, each verified against the prototype at four widths, up to the build handoff. Use when building prototype pages from Figma frames, starting or continuing a Webflow build (Designer through the MCP plus JS/CSS in the repo), building components or pages, comparing Webflow staging with the prototype, checking anchors, page transitions, load flicker or the published HTML, or wrapping up a build session. For project setup, the inputs and the Webflow snapshot, use webflow-prototype; for launch, webflow-launch; for a site that is already live, webflow-audit.
Webflow build: approved design → Webflow, section by section
The person approves every design decision; Claude builds, verifies and reports. A section is done only when it matches the approved design at every width, works after every way of arriving at it, and its manual steps are listed.
- checklists.md gates every step: the design handoff,
the Webflow foundation, the media page, each component, each finished
page and the build handoff. Progress on them is recorded in
docs/handoff/progress.jsonwithpnpm progress(checklists.md › How to record progress), which Bench's Tasks panel shows. - conventions.md sets the page and section structure, class and variable naming, and the component, CMS and style-guide patterns every build starts from.
- lessons/ holds what cost time on earlier builds, by area. Read the file for the area you are about to work in.
- The prototype comes first, always: it is built from the Figma file,
page by page, in Bench (or by Claude in the terminal with the same
steps), and the client approves it before anything is built in Webflow.
The steps are below (The prototype, from Figma); the
webflow-prototypeskill has project setup, the inputs, the snapshot and the style guide, and its figma.md how to read the file. - rules.md has the rules every Webflow skill shares (no commit or push without an OK, Designer first, no credentials in chat, manual steps, lessons), the session wrap-up and how to contribute back.
- starter-site.md describes the starter Webflow site a project duplicates, with the base system already in it.
- The
webflow-style-guideskill makes the style guide page, the first page of the prototype and of the Webflow build, in the one layout every site uses. - media.md is the media page: where a site's videos are uploaded so Webflow converts and hosts them, built before the first page that plays one.
- A site that is already live is not a build: use the
webflow-auditskill (audit, then small verified batches; also for one-off fixes). - After the build handoff, the
webflow-launchskill takes over: pre-launch QA, launch day, the client handoff and the first 30 days.
Every checklist item says who does it:
- [Claude] Claude does it (through the MCP, the repo or a script).
- [Claude checks] Claude verifies it and reports; a person fixes what Claude can't.
- [Person] a person does it (client, PM, designer, DNS, accounts). Claude may remind and track it, never claim it.
Session start (every session)
- Read the workspace
CLAUDE.md, then this repo'sAGENTS.mdandGOTCHAS.md, thendocs/handoff/(HANDOFF.md,MANUAL-TODO.md,BUILD-NOTES.md, andREVIEW.mdonce the review phase has started). Skim lessons/README.md and read the lesson file for today's area (MCP work:lessons/mcp.md). Never repeat a mistake already logged. - Read the project memory: where the last session stopped, open manual
steps, open questions. Run
pnpm progress showfor the stage, the checklists still open and the person's own to-dos. Say in one line where things stand and what's next; ask which page or section if it isn't clear. - Run Before building in
checklists.mdbefore the first Webflow batch; the Prototype stage before it is done (the inputs arewebflow-prototype's). Agree any change toconventions.mdwith the designer. If an input is missing, ask for it; don't guess content, states or SEO fields. - Webflow MCP: call
webflow_guide_toolonce, then reuse one session id and agent id for the whole session. - The prototype dev server is running and
webflow-build.config.jsonexists (pnpm new-projectwrites it;webflow-prototype› Project setup). - Today's scope is one page (see Working rhythm). Say which.
Standing rules
The shared rules are in rules.md. The build adds:
- Ask before creating, editing or removing classes and variables, one batch at a time, with names and values. A request that clearly implies them (e.g. "build it in Webflow" after approving a mock) is the OK.
- Publishing depends on whether the site is live.
- New build (no custom domain attached): publishing to the webflow.io staging domain is fine.
- Live site: a publish is site-wide. It sends out everything pending,
including other people's unfinished edits. Publish to staging only,
check what else would go live, and wait for the person to type
"publish" before the custom domain (
webflow-audithas the steps). - Never publish to a custom domain, cut a release or change DNS without asking.
- After every Webflow write batch, say two things: "reload the Designer" (an open Designer tab doesn't show MCP changes, and saving from a stale tab can overwrite them), and what is not published yet.
- Mark manual steps on the page. A
MANUAL-TODO.mdrow that leaves something visibly unfinished (a value still missing, a binding not made) gets a marker next to the element, or next to its specimen on the style guide: a Caption withdata-manual-todo="<letter>"and the text "Manual step : ". It shows on the canvas and on staging, so nobody reviews the unfinished state as the design. Remove it in the batch that ticks the row; none may be left at the build handoff or at launch (search the published HTML fordata-manual-todo).
The prototype, from Figma
Every site starts from a Figma file. The prototype is built from it, page by page, and approved before anything is built in Webflow.
In Bench. Add client › from a Figma design sets up the client folder,
the repo from this template and an empty prototype/ (a copy of
prototype-starter/).
Build the prototype from Figma groups the frames into pages; the person
picks and orders them, each with its address. One AI job then builds them
one page at a time: each page comes as its own message with its frames'
node ids. Each built page keeps its Figma frame for Compare › With Figma.
Bench looks at the file again when the client opens and hourly, and
offers to update the pages whose frames changed: change only what changed
in those frames, and keep edits made in the prototype since.
Not in Bench, Claude follows the same steps in the terminal: list the frames, agree the pages, their order and addresses with the person, then build one page at a time.
One page:
- Its frames. Frames with the same name across Desktop, Tablet and
Mobile are one page. Read each with the Figma connector
(
get_design_context,get_screenshot,get_variable_defs) or the Figma app's MCP server (figma.md). No Tablet frame: draw Tablet between Desktop and Mobile. No Mobile frame: make it work on a phone from the larger frames, and say so. Frames that aren't pages (the style guide) are read for tokens, type and components. - Tokens from the file's variables and text styles, as CSS custom
properties: changes to the snapshot's variables and new ones in
src/css/proposed.css(old → new), named byconventions.md§4; never in the generatedtokens.css. - Fonts the text styles use, in
public/fonts/with their licence, declared insrc/css/prototype.cssand preloaded inpartials/head.html. - Images exported from the file into
prototype/images/: photos as WebP at the largest size the design uses, icons as SVG. - Only what's in the file. A value, state, text or image the file
doesn't give is a question: ask before inventing it (in Bench, add it
with
mcp__bench__todo_addand carry on). - Where it goes:
prototype/<page>/index.htmlat its Webflow address (Home isprototype/index.html), added topagesinprototype/vite.config.js. Nav and footer are shared partials. Change only that page, plus the partials and tokens it needs. Don't commit, push or publish. - Check it beside its frames at Desktop, Tablet and Mobile, and that the Figma prototype's links work (menus, tabs, forms).
Then the page runs Prototype page done (webflow-prototype's
checklists.md) and the person
approves it (in Bench: right-click in Pages). The Prototype stage in
checklists.md tracks it all, up to the client's approval
in writing:
- each page matches its Figma frame at Desktop, Tablet and Mobile (Bench's "Matches the Figma frame" check);
- the Figma file is linked to the client;
- frames picked, in order, each with its page address;
- every picked frame built as a prototype page;
- tokens, fonts and images come from the file; nothing made up;
- the Figma prototype's links work;
- each page approved in the prototype;
- the client approved the prototype in writing.
The order of a build
It starts once the Prototype stage is done. Structure and names follow
conventions.md. Each step is gated in checklists.md:
- variables;
- tag styles (set once in the Designer so the MCP can reach them);
- classes;
- CMS collections;
- the repo port (modules and repo CSS from the prototype) and the loader, so staging runs the real code → Foundation done;
- the style guide page, the first page built
(
webflow-style-guideskill) → Style guide base done; - the media page, when any page plays a video (media.md) → Media done;
- components, built from the real page markup (never from style guide previews), each adding its style guide row → Component done;
- pages, section by section → Page done;
- the style guide final check, then Build handoff;
- the review phase:
docs/handoff/REVIEW.mdlists what is built per page, each feedback round and what is left before launch, until thewebflow-launchskill takes over.
A site duplicated from the starter already has the base of steps 1 to
3 and the page shell; the project adds to it. Before each import run
pnpm wf:preflight; plan class batches with pnpm wf:styles
(see Checks).
The section loop
For each section, in page order:
- Read the prototype: its markup, CSS and the module that animates
it, with the page's Figma frame for states and the interaction spec.
Note every
data-*hook, id and aria attribute. - Check what Webflow has: classes (
query_styles), components and their props, CMS fields, assets. Reuse before creating; name anything new byconventions.md(sections 2–4). - Build, preferring, in order:
- component instances;
- the element builder for DOM elements (
button,ul/liwhen ARIA roles must survive,svg,spanwith tag), with nested children; - WHTML import for plain markup, then fix what it drops (see the
importer checklist below).
Page content lives in the G | Main slot of the G | Page W
component. On a page whose slot is still empty: build the sections in
a To Slot wrapper after G | Page W, ask the person to drag the wrapper into the slot, then ungroup
it (move each child before the wrapper, remove the wrapper). Once a
section is in the slot, build next to it directly (
conventions.md§1). Check slot content withquery_elements; the page tree shows slots as empty.
- Wire attributes, CMS bindings, prop values, links, form settings.
- Publish, then run the checks. Fix, re-publish, re-check.
- Report: what was built, what matched, what differs and why, manual steps, and the files waiting for a commit OK.
When a page is complete, run Page done in checklists.md.
Importer checklist (after every WHTML import)
<button>became a Link → rebuild as DOMbutton, move children in.<img>lost every attribute and its asset link →set_image_asset, re-adddata-*,loading, size (or a class withaspect-ratio).<span>became a text Span → shapes as<div>; split text pieces as DOMspan(otherwise editing the parent's text flattens them).- A class list with one missing class lost all its classes → create
classes first, re-check
styleNames. - DOM elements keep a literal
classattribute →remove_attribute class. - A normal space before
<br>is trimmed →Line. <br>Next. - Boolean attributes need a value (
muted="true") or publishing drops them. - Lists publish as
role="list"→ keep ARIA roles on DOMul/li.
Canvas hygiene
- Every class on an empty element: padding 0 on all sides and
font-size: inherit(the canvas.wf-emptyadds 75px padding and font-size 0). - Every grid sets at least one row and one column template; centre single children with flex, not grid.
- An element drawn only by a pseudo-element (an icon font glyph) gets a
space as its text (
set_settings, keytext), so it is not empty on the canvas. Webflow publishes it as a non-breaking space, which takes room: size the element (1em × 1em) and align its content to flex-start so the space can't move the glyph. - No style values only the MCP can see: if the Designer can't display it, it belongs in repo CSS with a tag.
Checks
The scripts are in this skill's scripts/ folder, with a pnpm shortcut
each. They read webflow-build.config.json from the current directory or a
parent (the example is webflow-build.config.example.json). By default
staging loads this repo's local dist/, so code can be checked before a
push; --live checks what visitors get (seo, install, parity,
tracking and redirects check the custom domain in liveUrl with it;
domain always does). Screenshots go to
.webflow-build/ next to the config.
pnpm wf:compare /path '<section selector>' [width] [--scroll] # element geometry vs prototype
pnpm wf:pass /path [width] # whole page: section offsets + pixel frames
pnpm wf:anchors /path [width] [--offset N] # every #link lands on its section
pnpm wf:outline /path # heading outline, one h1, glued words at <br>
pnpm wf:film /path [selector] [width] # first seconds of a load (flicker)
pnpm wf:transitions /start /a /b [--width W] # Barba swaps, leaks, full page loads
pnpm wf:a11y [/path…] [--all-widths] # axe scan, WCAG 2.2 A/AA rules
pnpm wf:links [/path…] [--max 50] [--external] # crawl: broken, # and placeholder links
pnpm wf:baseline save|compare [/path…] # full-page visual baselines (launch, releases)
pnpm wf:html [/path…] # published HTML: what Webflow changed on publish
pnpm wf:perf [/path…] [--runs 5] [--mobile|--desktop] # Lighthouse, median of several runs
pnpm wf:seo [/path…] [--live] [--canonical] # sitemap crawl: titles, OG, one h1, JSON-LD, noindex
pnpm wf:install [/path…] [--live] # installed snippets vs loader.html, localhost requests
pnpm wf:parity [/path…] [--live] [--width W] [--control] # migration: old custom code vs dist/, every element
pnpm wf:overflow [/path…] [--live] [--width W[,W…]] [--why] # what runs off the right edge, 2560 … 360
pnpm wf:tracking [/path…] [--live] # tags, double page views, cookie consent, gclid/UTM (hits blocked)
pnpm wf:redirects map [--from URL] [--existing export.csv] # old site → new pages, Webflow 301 import file
pnpm wf:redirects test [--live] [--file urls.txt] # every redirect lands in one permanent hop
pnpm wf:domain # liveUrl: certificate, domain renewal, DNS host, http/www redirects
Two more read the prototype's files, with no browser and no Webflow
(--root <dir>, else prototypeDir in the config, default
../prototype):
pnpm wf:preflight [/path…] [--strict] # markup the importer or publisher will change: FIX now, or PLAN the step
pnpm wf:styles # new classes sorted into: connector / by hand in the Designer / repo CSS
Paths default to pages in the config, else /.
From outside the repo, run the same files with node .claude/skills/webflow-build/scripts/<name>.mjs.
Reading results:
compare: ±1px is rounding. "Only in webflow" rows for component wrappers are expected structure, not bugs.pass: video heroes differ frame to frame. Any other frame >2% is a real difference: open the PNG pair before guessing. A section offset that differs shifts everything after it, so fix the first one.anchors: every rowok; pinned targets are measured at their.pin-spacer.load-film: healthy = hidden → 0 → rising. Items at full opacity before the animation = flicker.transitions: ✓ on every swap, height and pins equal to the direct load after each round,full loads 1.a11y: exits 1 on serious or critical failures. Automated rules catch about a third of WCAG issues; keyboard and screen reader checks stay.links:#links are fine while content is missing, as long as each one is in the known issues.baseline:CHANGEDrows have before/after PNGs; expected changes →saveagain and commit.html: every finding is something no canvas or API read shows: a field published asname="field", a link standing in for a button, a variant whose class is on no element, a class no stylesheet defines (a conditional class with a typo), a script hook the prototype has and the published page lost. Exits 1 on any finding.perf: read the median, and the range next to it. A change smaller than the range is noise, not a result. It always measures the deployed bundle.seo: pages fromsitemap.xml, else a crawl frompages. Missing title, description or OG tags, not one<h1>, and JSON-LD that doesn't parse or holds an HTML entity (') fail; noindex pages are listed, and fail with--liveunlessseo.noindexOkallows them. Long titles, short descriptions and pages with no JSON-LD (outsideseo.noSchemaOk) are notes.install: a loader piece (head code, Embed 2a, 2b, footer code) that differs fromloader.htmlis an old paste: re-paste it and publish. Also fails on two viewport metas, two Finsweet Attributes tags and any request to localhost from the page.parity: every difference is a change the live site would see. Run--controlfirst: what differs between two renders of the published page is noise, forparity.ignore. Screenshot pairs are in.webflow-build/parity/.overflow: each row is the first element of a chain whose visible right edge passes the viewport, with the widths and the worst overshoot; a page that scrolls sideways is its own finding. The sweep covers the widths the three checks miss (1366, 1280, 1024, 992…), where the fluid scale holds and fixed sizes stop fitting. Clipped content (slider masks, marquees), off-canvas menus and hidden elements are skipped;--whylists them,overflow.ignoreadds selectors. A row marked "moved by a transform" may be an animation's start state: check it after the animation has played. Fixes are Designer fixes:minmax(0, 1fr)columns,min-width: 0on flex and grid children, wrapping instead of fixed widths.preflight: FIX means change the prototype markup now (an empty span as a shape, a control outside a form, a valueless boolean, an inline style, a class in no CSS file). PLAN means the markup is right and the build needs a known extra step (a button, a span inside text, image attributes, a placeholder).styles: the plan for the class batches, saved to.webflow-build/styles/styles-plan.json. connector lines are what to propose; every BY HAND line becomes a row inMANUAL-TODO.mdbefore the batch, not after; Repo CSS rules go tosrc/styles.csswith the tag shown. It writes nothing to Webflow.
Run preflight on a prototype page before it is approved and again before
its import; styles before the first class batch and whenever
new-classes*.css changes; compare per section while building; pass,
outline, anchors, a11y, links, html and overflow when a page is done;
perf at the build handoff and before and after any speed work;
install after every paste of a loader piece; seo before launch; load-film after touching the hero or the loader;
transitions after touching pages, links or modules. Check all three
widths (Webflow's breakpoints are 991/767/479). For interactions, script
the hover, click or scroll state and compare that, not only the resting
state.
Repo work
- One module per feature in
src/modules/, onerun()line in the manifest. Modules depend ondata-*attributes, not class names. pnpm check && pnpm build && pnpm testbefore asking to commit;dist/is committed with the source.- With page transitions (Barba), page modules run per page inside a
gsap.contextwith their listeners recorded and torn down on leave; global modules (Lenis, nav) run once and expose refresh hooks. Test after arriving by transition, not only on direct load. - Things visible at load:
gsap.set()+gsap.to(), nevergsap.from(). - Swapping a Webflow image: remove
srcset/sizesfirst. - Anchors: unbind webflow.js's
click.wf-scroll; scroll to a pinned section's.pin-spacer; pause scroll snapping during the glide. - Never put
backdrop-filter,filterortransformon a wrapper that holdsposition: fixedchildren. - Libraries are bundled, never Webflow script tags. Copy-in recipes: finsweet (Finsweet Attributes), phosphor, video, anchor-scroll, smooth-scroll (GSAP + Lenis), barba, modal, snippets (count-up, marquee, FAQ, tabs, dropdown close, resize reload, page-loader backstop, IX3 fallback).
- Sizes follow the fluid scaling in
src/styles.css§01: Webflow variables in em, the desktop frame set to the design before building. - Loader snippets aren't versioned: any
loader.htmlchange must be re-pasted in Webflow (a manual step).
Migrating a live site
Moving a site that is already live onto the template (its old custom code hosted elsewhere) happens in two phases, never one:
- Parity first. The port reproduces the live site with no visual,
content or behaviour change. Map each old custom-code asset in
parity.oldAssets(a URL regex →index.js,styles.css, or""for a library the bundle now carries), thenpnpm wf:parity --liveon every template page at every width until it reports zero differences, or each one is explained. Only then paste the loader and remove the old code, and runpnpm wf:install --live. - Improvements second, in later releases, one at a time, each
re-checked with
wf:parityand shown to the person.
Template defaults that change a live site are improvements, not parity:
check each before the first release and keep it off until approved. The
scroll lock (html.is-loading in the head code and src/styles.css),
the theme-color meta in the head code, the icon font in the bundle
(pnpm phosphor), and the base CSS (box-sizing, the §01 type scaling)
all show up in wf:parity if the live site didn't have them.
Working rhythm
- One page per session, finished completely: built, checked at every width, its states and SEO fields done, Page done run, handoff updated. Half-built pages are where shortcuts get forgotten.
- Page-by-page approval. The client approves each prototype page (after Prototype page done), then the whole prototype in writing, before its Webflow build; the designer approves each Webflow page before the next one starts.
- A go-ahead before each Webflow batch. Propose the batch (variables old → new, classes with what they are for, the elements to build), wait for the OK, write it, report what changed. Approval of one batch never covers the next.
docs/handoff/stays current during the session, not at the end: a decision goes intoHANDOFF.mdwhen it is made, a manual step intoMANUAL-TODO.mdwhen it is found.- After the build handoff, review. Fill in
docs/handoff/REVIEW.md: what is built per page, then one table per feedback round, the next freeMANUAL-TODO.mdletter and the list of what has to happen before launch. - Lessons go back. At wrap-up, offer every
template-candidatelesson to the template (rules.md › Contributing).
Session wrap-up and contributing
Both are in rules.md.