Vibecode Framer
track this build5 phases, 15 steps, beginner friendly0%You can build a simple AI/site generator and host static pages, but Framer's animation/design editor, CMS, hosting, templates, and publishing polish are real product depth.
You are building a lean indie version of Framer. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Framer · indie build A visual editor over a layout tree rather than a canvas of positioned boxes, reusable components with overridable content, breakpoints that adjust rules rather than duplicating designs, and a static export of real HTML and CSS that loads fast and works without JavaScript for its content. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with Vite for the editor | one stateful editing surface where a component tree gets in the way | | Model | A layout tree with flow containers, never stored positions | this is the constraint the whole build rests on | | Preview | Real HTML and CSS rendered in an iframe | previewing with the same engine that will render the output is the only way they cannot disagree | | Output | Static HTML and CSS files, with JavaScript optional | a site that needs JavaScript to show its text is slower and more fragile than it needs to be | | Storage | A JSON document per site, plus assets | the site definition is a file you own and can diff | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the editor build and the asset pipeline. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A real site design you want to build** · free - Why: Layout containers are only tested by a real design, which will contain the one arrangement that makes you want an absolute position. Solving that with a container type is the point. - Get it: Take a site you designed or admire and rebuild it. The section that resists is where the container model needs work. - Verify: A real multi-section page is rebuilt without any positioned element - [ ] **Real photographs for the page** · free - Why: Page weight and responsive images are invisible with placeholder graphics and immediately obvious with real photographs. - Get it: Use images you have the rights to, at the sizes they came from the camera. - Verify: A page with photographs reports its weight - [ ] **A static host and a domain** (optional) · often free hosting; roughly 10 to 15 US dollars a year for a domain - Why: Product phase only. The output is plain files and needs somewhere to live with a certificate. - Get it: Any static hosting with automatic certificates, plus a domain from any registrar. - Verify: The site serves over HTTPS - [ ] **A decision about where form submissions go** (optional) · free with your own endpoint - Why: A static site cannot receive a submission by itself, and the easy answer sends your visitors' data to a service you have not evaluated. - Get it: Decide before publishing a form: your own endpoint, or a service whose terms and retention you have read. Record the decision. - Verify: The destination and its retention are documented ## Quick start ```sh npm create vite@latest . -- --template vanilla-ts ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The visual design polish of the commercial editor. - Animation and interaction design. - The template marketplace. - Managed hosting with a delivery network. - visual design polish - animations - templates - CMS - hosting - preview/publish flow - forms If one of those is essential to you, that is the reason to keep paying for Framer, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Framer The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a visual site builder producing static output, where responsive means layout rules rather than three fixed designs to replace Framer. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Absolute positioning is the trap that kills visual site builders. Dragging an element to a spot on a canvas produces a design that is correct at one width and wrong at every other, and the usual remedy is to draw the whole page again at two more widths and maintain three designs forever. So the editor never stores a position. Elements go into layout containers with rules, and moving something means changing its place in the tree or its rule. That constraint makes the editor slightly harder to build and makes responsive output a consequence rather than a separate project. ### Stack (fixed, do not substitute) - Runtime: TypeScript with Vite for the editor · one stateful editing surface where a component tree gets in the way - Model: A layout tree with flow containers, never stored positions · this is the constraint the whole build rests on - Preview: Real HTML and CSS rendered in an iframe · previewing with the same engine that will render the output is the only way they cannot disagree - Output: Static HTML and CSS files, with JavaScript optional · a site that needs JavaScript to show its text is slower and more fragile than it needs to be - Storage: A JSON document per site, plus assets · the site definition is a file you own and can diff ### Data model (create this before Phase 1) - sites(id, name, updated_at), pages(id, site_id, path, title, meta_json) - nodes(id, page_id, parent_id, ordinal, kind, props_json, style_rules_json) · kind is stack, grid, text, image, link or component_instance - components(id, site_id, name, root_node_id), instances(node_id, component_id, overrides_json) - breakpoints(id, site_id, name, min_width, ordinal) - style_rules · per node per breakpoint, adjusting the rule rather than redefining the node - assets(id, site_id, path, kind, width, height, bytes) - No node has x and y. A node has a parent, an order and layout rules, which is what makes one design work at every width. ### Phase 1 · A layout tree with no coordinates Build: Stack and grid containers with direction, spacing, padding and alignment. Drag to reparent and reorder rather than to position. Render the tree as real HTML and CSS in an iframe preview. Trap: Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. Done when: No node stores x or y coordinates, verified by inspecting the document. Dragging an element reparents or reorders it and the preview reflects the change. Resizing the preview reflows the page without any breakpoint work. The rendered preview uses the same HTML and CSS the export will produce. Do not build yet: No components, breakpoints or export. ### Phase 2 · Components with overrides Build: Turn a subtree into a component. Instances resolve from the component and store only their overrides. Editing the component updates instances that have not overridden the changed property. Trap: Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. Done when: Editing a component's text updates instances that have not overridden it and leaves overridden ones alone. An override survives an unrelated component change. Deleting a component leaves instances in a visibly broken state naming what is missing. Nesting a component inside another resolves correctly. Do not build yet: No breakpoints or export yet. ### Phase 3 · Breakpoints that adjust rather than duplicate Build: Breakpoints as ordered minimum widths. A node's rules can be overridden per breakpoint, inheriting from the one below where nothing is specified. Never duplicate the tree per breakpoint. Trap: Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. Done when: A node with a rule at one breakpoint inherits it at wider ones unless overridden. Changing content changes it at every breakpoint because there is one tree, verified by editing text and checking all widths. The export produces media queries matching the breakpoints. Adding a breakpoint does not duplicate any node. Do not build yet: No assets or export yet. ### Phase 4 · Images that do not ruin the page weight Build: Import images, generate several sizes, and emit responsive image markup so a phone downloads a phone-sized file. Set explicit dimensions so the page does not shift as images load. Report page weight per page. Trap: Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. Done when: A page with images emits responsive markup and a narrow viewport downloads a smaller file, verified in developer tools. Images have explicit dimensions and the page does not shift as they load. Page weight is reported per page. An oversized import is downscaled with a note. Do not build yet: No export or deployment yet. ### Phase 5 · Static export that works without scripts Build: Export static HTML and CSS per page with real markup: headings as headings, links as links, and content in the document rather than injected. Any interactivity is added on top and degrades. Verify with scripting disabled. Trap: A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. Done when: An exported page shows all its content with scripting disabled. Headings, links and lists use semantic elements rather than generic containers. The export contains no build-time framework runtime. Exporting twice produces identical files. Do not build yet: No deployment or forms yet. ### Phase 6 · Publishing, forms and the things a site needs Build: Deploy the static output to a host with a certificate. Add a form that posts somewhere you control, since a static site cannot handle a submission alone. Keep the site definition in version control so a change is reviewable. Trap: A form on a static site needs somewhere to post, and wiring it to a third party endpoint sends your visitors' submissions to a service you have not thought about. Decide where submissions go and what happens to them before publishing the form rather than after the first one arrives. Done when: The site serves over HTTPS with a valid certificate. A form submission reaches a destination you control and the visitor sees a confirmation. The site definition is in version control and a change produces a readable diff. Republishing is one command. Do not build yet: Nothing after this. ### Out of scope (and why) - The visual design polish of the commercial product, which is years of attention to how the editor feels and is most of why designers pay for it. - Animation and interaction design, which is a substantial product of its own and where this category's real difficulty lives. - The template marketplace, which is content rather than code. - Managed hosting with a content delivery network, which is now a static host you choose and configure. ### README must contain - That no node stores coordinates, and why absolute positioning is refused - That breakpoints override rules on one tree rather than duplicating designs - That the preview uses the same renderer as the export - That exported pages show their content with scripting disabled - Where form submissions go and what happens to them ===== AGENTS.md ===== # Agent instructions · Framer indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: TypeScript with Vite for the editor, A layout tree with flow containers, never stored positions, Real HTML and CSS rendered in an iframe, Static HTML and CSS files, with JavaScript optional, A JSON document per site, plus assets. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. - Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. - Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. - Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. - A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. ===== BUILD_PLAN.md ===== # Build plan · Framer A visual editor over a layout tree rather than a canvas of positioned boxes, reusable components with overridable content, breakpoints that adjust rules rather than duplicating designs, and a static export of real HTML and CSS that loads fast and works without JavaScript for its content. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · A layout tree with no coordinates You can build a page visually and nothing stores a position. ### Steps 1. Model containers and flow Stack and grid with real rules. Direction, gap, padding, alignment. Files: `src/model.ts` ```sh npm create vite@latest . -- --template vanilla-ts ``` 2. Make dragging reparent, not position Drop targets are containers and positions in an order, not points on a canvas. Files: `src/drag.ts` 3. Preview with the export renderer The same code path. Two renderers diverge and the difference always appears after publishing. Files: `src/render.ts` ### Done when - [ ] No node stores coordinates - [ ] Dragging reparents or reorders - [ ] Resizing the preview reflows without breakpoint work - [ ] Preview and export use the same renderer ### Watch out - Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. ## Phase 2 · Components with overrides Changing a component changes every instance, except where an instance deliberately differs. ### Steps 1. Store sparse overrides An instance is a component reference plus the properties it changed. Nothing else. Files: `src/components.ts` 2. Resolve at render Every render, so propagation is automatic rather than a sync step. Files: `src/resolve.ts` 3. Handle a missing component visibly Name what is missing. A silent hole is worse than a visible error. Files: `src/resolve.ts` ### Done when - [ ] A component change propagates to non-overridden instances - [ ] An override survives unrelated changes - [ ] A deleted component leaves a named broken state - [ ] Nested components resolve ### Watch out - Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. ## Phase 3 · Breakpoints that adjust rather than duplicate A narrow screen gets a different layout without a second design to maintain. ### Steps 1. Model breakpoints as rule overrides Per node per breakpoint, inheriting upward. One tree, several rule sets. Files: `src/breakpoints.ts` 2. Preview at each width Switchable in the editor, using the same renderer. Files: `src/preview.ts` 3. Verify content is shared Edit text once and check every width. If you have to edit twice, the tree got duplicated. ### Done when - [ ] Rules inherit upward unless overridden - [ ] A content edit applies at every breakpoint - [ ] The export produces matching media queries - [ ] Adding a breakpoint duplicates no nodes ### Watch out - Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. ## Phase 4 · Images that do not ruin the page weight A page with photographs still loads quickly on a phone. ### Steps 1. Generate sizes on import Several widths, plus a modern format alongside a fallback. Files: `src/assets.ts` 2. Emit responsive markup So the browser picks. Serving one size to everyone is the default failure. Files: `src/render.ts` 3. Report page weight Per page, in the editor. Visible while building rather than discovered after launch. Files: `src/ui/Weight.tsx` ### Done when - [ ] A narrow viewport downloads a smaller image - [ ] Images have explicit dimensions and the page does not shift - [ ] Page weight is reported per page - [ ] Oversized imports are downscaled ### Watch out - Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. ## Phase 5 · Static export that works without scripts The published site shows its content with JavaScript disabled. ### Steps 1. Emit semantic markup Map node kinds to real elements. Generic containers everywhere is the tell of a bad builder. Files: `src/export.ts` 2. Put content in the document Not injected. Verify by disabling scripting and reloading. 3. Make output deterministic Two exports identical, so a diff shows real changes. Files: `src/export.test.ts` ### Done when - [ ] All content shows with scripting disabled - [ ] Semantic elements are used rather than generic containers - [ ] No framework runtime is included - [ ] Two exports are identical ### Watch out - A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. ## Not in this build - The visual design polish of the commercial editor. - Animation and interaction design. - The template marketplace. - Managed hosting with a delivery network. ## After v1, if you want it - A content collection so a blog does not mean duplicating a page per post - Design tokens for colour and spacing, applied by reference across the site - A visual diff between two versions of a page, since the definition is already diffable ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. The site definition. Version controlled, so a design change is a reviewable diff. VITE_SITE_PATH=./sites/mysite.json # Required. Source images before size generation. VITE_ASSETS_DIR=./assets # Required. Sizes generated per image for responsive markup. VITE_IMAGE_WIDTHS=480,960,1440,1920 # Required. Page weight at which the editor warns. Visible while building rather than after publishing. VITE_PAGE_WEIGHT_WARN_KB=600 # Required. Where static output is written. EXPORT_DIR=./dist # Optional. Where submissions post. Yours, or a service whose retention you have read. FORM_ENDPOINT=https://forms.example.com/submit
You are building a lean indie version of Framer. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Framer · indie build A visual editor over a layout tree rather than a canvas of positioned boxes, reusable components with overridable content, breakpoints that adjust rules rather than duplicating designs, and a static export of real HTML and CSS that loads fast and works without JavaScript for its content. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with Vite for the editor | one stateful editing surface where a component tree gets in the way | | Model | A layout tree with flow containers, never stored positions | this is the constraint the whole build rests on | | Preview | Real HTML and CSS rendered in an iframe | previewing with the same engine that will render the output is the only way they cannot disagree | | Output | Static HTML and CSS files, with JavaScript optional | a site that needs JavaScript to show its text is slower and more fragile than it needs to be | | Storage | A JSON document per site, plus assets | the site definition is a file you own and can diff | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the editor build and the asset pipeline. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A real site design you want to build** · free - Why: Layout containers are only tested by a real design, which will contain the one arrangement that makes you want an absolute position. Solving that with a container type is the point. - Get it: Take a site you designed or admire and rebuild it. The section that resists is where the container model needs work. - Verify: A real multi-section page is rebuilt without any positioned element - [ ] **Real photographs for the page** · free - Why: Page weight and responsive images are invisible with placeholder graphics and immediately obvious with real photographs. - Get it: Use images you have the rights to, at the sizes they came from the camera. - Verify: A page with photographs reports its weight - [ ] **A static host and a domain** (optional) · often free hosting; roughly 10 to 15 US dollars a year for a domain - Why: Product phase only. The output is plain files and needs somewhere to live with a certificate. - Get it: Any static hosting with automatic certificates, plus a domain from any registrar. - Verify: The site serves over HTTPS - [ ] **A decision about where form submissions go** (optional) · free with your own endpoint - Why: A static site cannot receive a submission by itself, and the easy answer sends your visitors' data to a service you have not evaluated. - Get it: Decide before publishing a form: your own endpoint, or a service whose terms and retention you have read. Record the decision. - Verify: The destination and its retention are documented ## Quick start ```sh npm create vite@latest . -- --template vanilla-ts ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The visual design polish of the commercial editor. - Animation and interaction design. - The template marketplace. - Managed hosting with a delivery network. - visual design polish - animations - templates - CMS - hosting - preview/publish flow - forms If one of those is essential to you, that is the reason to keep paying for Framer, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Framer The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a visual site builder producing static output, where responsive means layout rules rather than three fixed designs to replace Framer. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Absolute positioning is the trap that kills visual site builders. Dragging an element to a spot on a canvas produces a design that is correct at one width and wrong at every other, and the usual remedy is to draw the whole page again at two more widths and maintain three designs forever. So the editor never stores a position. Elements go into layout containers with rules, and moving something means changing its place in the tree or its rule. That constraint makes the editor slightly harder to build and makes responsive output a consequence rather than a separate project. ### Stack (fixed, do not substitute) - Runtime: TypeScript with Vite for the editor · one stateful editing surface where a component tree gets in the way - Model: A layout tree with flow containers, never stored positions · this is the constraint the whole build rests on - Preview: Real HTML and CSS rendered in an iframe · previewing with the same engine that will render the output is the only way they cannot disagree - Output: Static HTML and CSS files, with JavaScript optional · a site that needs JavaScript to show its text is slower and more fragile than it needs to be - Storage: A JSON document per site, plus assets · the site definition is a file you own and can diff ### Data model (create this before Phase 1) - sites(id, name, updated_at), pages(id, site_id, path, title, meta_json) - nodes(id, page_id, parent_id, ordinal, kind, props_json, style_rules_json) · kind is stack, grid, text, image, link or component_instance - components(id, site_id, name, root_node_id), instances(node_id, component_id, overrides_json) - breakpoints(id, site_id, name, min_width, ordinal) - style_rules · per node per breakpoint, adjusting the rule rather than redefining the node - assets(id, site_id, path, kind, width, height, bytes) - No node has x and y. A node has a parent, an order and layout rules, which is what makes one design work at every width. ### Phase 1 · A layout tree with no coordinates Build: Stack and grid containers with direction, spacing, padding and alignment. Drag to reparent and reorder rather than to position. Render the tree as real HTML and CSS in an iframe preview. Trap: Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. Done when: No node stores x or y coordinates, verified by inspecting the document. Dragging an element reparents or reorders it and the preview reflects the change. Resizing the preview reflows the page without any breakpoint work. The rendered preview uses the same HTML and CSS the export will produce. Do not build yet: No components, breakpoints or export. ### Phase 2 · Components with overrides Build: Turn a subtree into a component. Instances resolve from the component and store only their overrides. Editing the component updates instances that have not overridden the changed property. Trap: Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. Done when: Editing a component's text updates instances that have not overridden it and leaves overridden ones alone. An override survives an unrelated component change. Deleting a component leaves instances in a visibly broken state naming what is missing. Nesting a component inside another resolves correctly. Do not build yet: No breakpoints or export yet. ### Phase 3 · Breakpoints that adjust rather than duplicate Build: Breakpoints as ordered minimum widths. A node's rules can be overridden per breakpoint, inheriting from the one below where nothing is specified. Never duplicate the tree per breakpoint. Trap: Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. Done when: A node with a rule at one breakpoint inherits it at wider ones unless overridden. Changing content changes it at every breakpoint because there is one tree, verified by editing text and checking all widths. The export produces media queries matching the breakpoints. Adding a breakpoint does not duplicate any node. Do not build yet: No assets or export yet. ### Phase 4 · Images that do not ruin the page weight Build: Import images, generate several sizes, and emit responsive image markup so a phone downloads a phone-sized file. Set explicit dimensions so the page does not shift as images load. Report page weight per page. Trap: Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. Done when: A page with images emits responsive markup and a narrow viewport downloads a smaller file, verified in developer tools. Images have explicit dimensions and the page does not shift as they load. Page weight is reported per page. An oversized import is downscaled with a note. Do not build yet: No export or deployment yet. ### Phase 5 · Static export that works without scripts Build: Export static HTML and CSS per page with real markup: headings as headings, links as links, and content in the document rather than injected. Any interactivity is added on top and degrades. Verify with scripting disabled. Trap: A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. Done when: An exported page shows all its content with scripting disabled. Headings, links and lists use semantic elements rather than generic containers. The export contains no build-time framework runtime. Exporting twice produces identical files. Do not build yet: No deployment or forms yet. ### Phase 6 · Publishing, forms and the things a site needs Build: Deploy the static output to a host with a certificate. Add a form that posts somewhere you control, since a static site cannot handle a submission alone. Keep the site definition in version control so a change is reviewable. Trap: A form on a static site needs somewhere to post, and wiring it to a third party endpoint sends your visitors' submissions to a service you have not thought about. Decide where submissions go and what happens to them before publishing the form rather than after the first one arrives. Done when: The site serves over HTTPS with a valid certificate. A form submission reaches a destination you control and the visitor sees a confirmation. The site definition is in version control and a change produces a readable diff. Republishing is one command. Do not build yet: Nothing after this. ### Out of scope (and why) - The visual design polish of the commercial product, which is years of attention to how the editor feels and is most of why designers pay for it. - Animation and interaction design, which is a substantial product of its own and where this category's real difficulty lives. - The template marketplace, which is content rather than code. - Managed hosting with a content delivery network, which is now a static host you choose and configure. ### README must contain - That no node stores coordinates, and why absolute positioning is refused - That breakpoints override rules on one tree rather than duplicating designs - That the preview uses the same renderer as the export - That exported pages show their content with scripting disabled - Where form submissions go and what happens to them ===== AGENTS.md ===== # Agent instructions · Framer indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: TypeScript with Vite for the editor, A layout tree with flow containers, never stored positions, Real HTML and CSS rendered in an iframe, Static HTML and CSS files, with JavaScript optional, A JSON document per site, plus assets. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. - Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. - Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. - Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. - A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. ===== BUILD_PLAN.md ===== # Build plan · Framer A visual editor over a layout tree rather than a canvas of positioned boxes, reusable components with overridable content, breakpoints that adjust rules rather than duplicating designs, and a static export of real HTML and CSS that loads fast and works without JavaScript for its content. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · A layout tree with no coordinates You can build a page visually and nothing stores a position. ### Steps 1. Model containers and flow Stack and grid with real rules. Direction, gap, padding, alignment. Files: `src/model.ts` ```sh npm create vite@latest . -- --template vanilla-ts ``` 2. Make dragging reparent, not position Drop targets are containers and positions in an order, not points on a canvas. Files: `src/drag.ts` 3. Preview with the export renderer The same code path. Two renderers diverge and the difference always appears after publishing. Files: `src/render.ts` ### Done when - [ ] No node stores coordinates - [ ] Dragging reparents or reorders - [ ] Resizing the preview reflows without breakpoint work - [ ] Preview and export use the same renderer ### Watch out - Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. ## Phase 2 · Components with overrides Changing a component changes every instance, except where an instance deliberately differs. ### Steps 1. Store sparse overrides An instance is a component reference plus the properties it changed. Nothing else. Files: `src/components.ts` 2. Resolve at render Every render, so propagation is automatic rather than a sync step. Files: `src/resolve.ts` 3. Handle a missing component visibly Name what is missing. A silent hole is worse than a visible error. Files: `src/resolve.ts` ### Done when - [ ] A component change propagates to non-overridden instances - [ ] An override survives unrelated changes - [ ] A deleted component leaves a named broken state - [ ] Nested components resolve ### Watch out - Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. ## Phase 3 · Breakpoints that adjust rather than duplicate A narrow screen gets a different layout without a second design to maintain. ### Steps 1. Model breakpoints as rule overrides Per node per breakpoint, inheriting upward. One tree, several rule sets. Files: `src/breakpoints.ts` 2. Preview at each width Switchable in the editor, using the same renderer. Files: `src/preview.ts` 3. Verify content is shared Edit text once and check every width. If you have to edit twice, the tree got duplicated. ### Done when - [ ] Rules inherit upward unless overridden - [ ] A content edit applies at every breakpoint - [ ] The export produces matching media queries - [ ] Adding a breakpoint duplicates no nodes ### Watch out - Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. ## Phase 4 · Images that do not ruin the page weight A page with photographs still loads quickly on a phone. ### Steps 1. Generate sizes on import Several widths, plus a modern format alongside a fallback. Files: `src/assets.ts` 2. Emit responsive markup So the browser picks. Serving one size to everyone is the default failure. Files: `src/render.ts` 3. Report page weight Per page, in the editor. Visible while building rather than discovered after launch. Files: `src/ui/Weight.tsx` ### Done when - [ ] A narrow viewport downloads a smaller image - [ ] Images have explicit dimensions and the page does not shift - [ ] Page weight is reported per page - [ ] Oversized imports are downscaled ### Watch out - Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. ## Phase 5 · Static export that works without scripts The published site shows its content with JavaScript disabled. ### Steps 1. Emit semantic markup Map node kinds to real elements. Generic containers everywhere is the tell of a bad builder. Files: `src/export.ts` 2. Put content in the document Not injected. Verify by disabling scripting and reloading. 3. Make output deterministic Two exports identical, so a diff shows real changes. Files: `src/export.test.ts` ### Done when - [ ] All content shows with scripting disabled - [ ] Semantic elements are used rather than generic containers - [ ] No framework runtime is included - [ ] Two exports are identical ### Watch out - A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. ## Not in this build - The visual design polish of the commercial editor. - Animation and interaction design. - The template marketplace. - Managed hosting with a delivery network. ## After v1, if you want it - A content collection so a blog does not mean duplicating a page per post - Design tokens for colour and spacing, applied by reference across the site - A visual diff between two versions of a page, since the definition is already diffable ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. The site definition. Version controlled, so a design change is a reviewable diff. VITE_SITE_PATH=./sites/mysite.json # Required. Source images before size generation. VITE_ASSETS_DIR=./assets # Required. Sizes generated per image for responsive markup. VITE_IMAGE_WIDTHS=480,960,1440,1920 # Required. Page weight at which the editor warns. Visible while building rather than after publishing. VITE_PAGE_WEIGHT_WARN_KB=600 # Required. Where static output is written. EXPORT_DIR=./dist # Optional. Where submissions post. Yours, or a service whose retention you have read. FORM_ENDPOINT=https://forms.example.com/submit
You are building a production product version of Framer. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Framer · product brief ## Problem You can build a simple AI/site generator and host static pages, but Framer's animation/design editor, CMS, hosting, templates, and publishing polish are real product depth. ## Product outcome A visual builder whose output is one responsive design rather than three, whose pages show their content without scripts, and whose site definition can be reviewed as a diff. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - React/site builder - CMS/storage - static exporter - hosting - domain/DNS - optional LLM API ## Explicit non-goals for v1 - The visual design polish of the commercial editor. - Animation and interaction design. - The template marketplace. - Managed hosting with a delivery network. - visual design polish - animations - templates - CMS - hosting - preview/publish flow - forms ## Success criteria - No node stores coordinates - A content edit is confirmed to apply at every breakpoint - An exported page is confirmed to show content with scripting disabled - A narrow viewport is confirmed to download a smaller image - Two exports are byte-identical ===== BRIEF.md ===== # Build brief · Framer The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a visual site builder producing static output, where responsive means layout rules rather than three fixed designs to replace Framer. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Absolute positioning is the trap that kills visual site builders. Dragging an element to a spot on a canvas produces a design that is correct at one width and wrong at every other, and the usual remedy is to draw the whole page again at two more widths and maintain three designs forever. So the editor never stores a position. Elements go into layout containers with rules, and moving something means changing its place in the tree or its rule. That constraint makes the editor slightly harder to build and makes responsive output a consequence rather than a separate project. ### Stack (fixed, do not substitute) - Runtime: TypeScript with Vite for the editor · one stateful editing surface where a component tree gets in the way - Model: A layout tree with flow containers, never stored positions · this is the constraint the whole build rests on - Preview: Real HTML and CSS rendered in an iframe · previewing with the same engine that will render the output is the only way they cannot disagree - Output: Static HTML and CSS files, with JavaScript optional · a site that needs JavaScript to show its text is slower and more fragile than it needs to be - Storage: A JSON document per site, plus assets · the site definition is a file you own and can diff ### Data model (create this before Phase 1) - sites(id, name, updated_at), pages(id, site_id, path, title, meta_json) - nodes(id, page_id, parent_id, ordinal, kind, props_json, style_rules_json) · kind is stack, grid, text, image, link or component_instance - components(id, site_id, name, root_node_id), instances(node_id, component_id, overrides_json) - breakpoints(id, site_id, name, min_width, ordinal) - style_rules · per node per breakpoint, adjusting the rule rather than redefining the node - assets(id, site_id, path, kind, width, height, bytes) - No node has x and y. A node has a parent, an order and layout rules, which is what makes one design work at every width. ### Phase 1 · A layout tree with no coordinates Build: Stack and grid containers with direction, spacing, padding and alignment. Drag to reparent and reorder rather than to position. Render the tree as real HTML and CSS in an iframe preview. Trap: Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. Done when: No node stores x or y coordinates, verified by inspecting the document. Dragging an element reparents or reorders it and the preview reflects the change. Resizing the preview reflows the page without any breakpoint work. The rendered preview uses the same HTML and CSS the export will produce. Do not build yet: No components, breakpoints or export. ### Phase 2 · Components with overrides Build: Turn a subtree into a component. Instances resolve from the component and store only their overrides. Editing the component updates instances that have not overridden the changed property. Trap: Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. Done when: Editing a component's text updates instances that have not overridden it and leaves overridden ones alone. An override survives an unrelated component change. Deleting a component leaves instances in a visibly broken state naming what is missing. Nesting a component inside another resolves correctly. Do not build yet: No breakpoints or export yet. ### Phase 3 · Breakpoints that adjust rather than duplicate Build: Breakpoints as ordered minimum widths. A node's rules can be overridden per breakpoint, inheriting from the one below where nothing is specified. Never duplicate the tree per breakpoint. Trap: Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. Done when: A node with a rule at one breakpoint inherits it at wider ones unless overridden. Changing content changes it at every breakpoint because there is one tree, verified by editing text and checking all widths. The export produces media queries matching the breakpoints. Adding a breakpoint does not duplicate any node. Do not build yet: No assets or export yet. ### Phase 4 · Images that do not ruin the page weight Build: Import images, generate several sizes, and emit responsive image markup so a phone downloads a phone-sized file. Set explicit dimensions so the page does not shift as images load. Report page weight per page. Trap: Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. Done when: A page with images emits responsive markup and a narrow viewport downloads a smaller file, verified in developer tools. Images have explicit dimensions and the page does not shift as they load. Page weight is reported per page. An oversized import is downscaled with a note. Do not build yet: No export or deployment yet. ### Phase 5 · Static export that works without scripts Build: Export static HTML and CSS per page with real markup: headings as headings, links as links, and content in the document rather than injected. Any interactivity is added on top and degrades. Verify with scripting disabled. Trap: A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. Done when: An exported page shows all its content with scripting disabled. Headings, links and lists use semantic elements rather than generic containers. The export contains no build-time framework runtime. Exporting twice produces identical files. Do not build yet: No deployment or forms yet. ### Phase 6 · Publishing, forms and the things a site needs Build: Deploy the static output to a host with a certificate. Add a form that posts somewhere you control, since a static site cannot handle a submission alone. Keep the site definition in version control so a change is reviewable. Trap: A form on a static site needs somewhere to post, and wiring it to a third party endpoint sends your visitors' submissions to a service you have not thought about. Decide where submissions go and what happens to them before publishing the form rather than after the first one arrives. Done when: The site serves over HTTPS with a valid certificate. A form submission reaches a destination you control and the visitor sees a confirmation. The site definition is in version control and a change produces a readable diff. Republishing is one command. Do not build yet: Nothing after this. ### Out of scope (and why) - The visual design polish of the commercial product, which is years of attention to how the editor feels and is most of why designers pay for it. - Animation and interaction design, which is a substantial product of its own and where this category's real difficulty lives. - The template marketplace, which is content rather than code. - Managed hosting with a content delivery network, which is now a static host you choose and configure. ### README must contain - That no node stores coordinates, and why absolute positioning is refused - That breakpoints override rules on one tree rather than duplicating designs - That the preview uses the same renderer as the export - That exported pages show their content with scripting disabled - Where form submissions go and what happens to them ===== ARCHITECTURE.md ===== # Architecture · Framer ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with Vite for the editor | one stateful editing surface where a component tree gets in the way | | Model | A layout tree with flow containers, never stored positions | this is the constraint the whole build rests on | | Preview | Real HTML and CSS rendered in an iframe | previewing with the same engine that will render the output is the only way they cannot disagree | | Output | Static HTML and CSS files, with JavaScript optional | a site that needs JavaScript to show its text is slower and more fragile than it needs to be | | Storage | A JSON document per site, plus assets | the site definition is a file you own and can diff | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Layout tree | Containers, order and rules. | Not swappable for a positioned canvas. It is the constraint that produces responsive output for free. | | Components | Reuse with sparse overrides. | Any implementation resolving at render. Copies break propagation. | | Breakpoints | Rule overrides on one tree. | Any override model, provided the tree is never duplicated. | | Assets | Sizes and responsive markup. | Any pipeline. Reporting weight in the editor is what keeps pages fast. | | Export | Semantic static output. | Any renderer, provided it is the same one the preview uses. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `VITE_SITE_PATH` · required · The site definition. Version controlled, so a design change is a reviewable diff. - `VITE_ASSETS_DIR` · required · Source images before size generation. - `VITE_IMAGE_WIDTHS` · required · Sizes generated per image for responsive markup. - `VITE_PAGE_WEIGHT_WARN_KB` · required · Page weight at which the editor warns. Visible while building rather than after publishing. - `EXPORT_DIR` · required · Where static output is written. - `FORM_ENDPOINT` · optional · Where submissions post. Yours, or a service whose retention you have read. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Framer product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: TypeScript with Vite for the editor, A layout tree with flow containers, never stored positions, Real HTML and CSS rendered in an iframe, Static HTML and CSS files, with JavaScript optional, A JSON document per site, plus assets. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. - Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. - Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. - Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. - A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. - A form on a static site needs somewhere to post, and wiring it to a third party endpoint sends your visitors' submissions to a service you have not thought about. Decide where submissions go and what happens to them before publishing the form rather than after the first one arrives. ===== MILESTONES.md ===== # Delivery milestones · Framer Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · A layout tree with no coordinates You can build a page visually and nothing stores a position. ### Steps 1. Model containers and flow Stack and grid with real rules. Direction, gap, padding, alignment. Files: `src/model.ts` ```sh npm create vite@latest . -- --template vanilla-ts ``` 2. Make dragging reparent, not position Drop targets are containers and positions in an order, not points on a canvas. Files: `src/drag.ts` 3. Preview with the export renderer The same code path. Two renderers diverge and the difference always appears after publishing. Files: `src/render.ts` ### Done when - [ ] No node stores coordinates - [ ] Dragging reparents or reorders - [ ] Resizing the preview reflows without breakpoint work - [ ] Preview and export use the same renderer ### Watch out - Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. ## M2 · Components with overrides Changing a component changes every instance, except where an instance deliberately differs. ### Steps 1. Store sparse overrides An instance is a component reference plus the properties it changed. Nothing else. Files: `src/components.ts` 2. Resolve at render Every render, so propagation is automatic rather than a sync step. Files: `src/resolve.ts` 3. Handle a missing component visibly Name what is missing. A silent hole is worse than a visible error. Files: `src/resolve.ts` ### Done when - [ ] A component change propagates to non-overridden instances - [ ] An override survives unrelated changes - [ ] A deleted component leaves a named broken state - [ ] Nested components resolve ### Watch out - Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. ## M3 · Breakpoints that adjust rather than duplicate A narrow screen gets a different layout without a second design to maintain. ### Steps 1. Model breakpoints as rule overrides Per node per breakpoint, inheriting upward. One tree, several rule sets. Files: `src/breakpoints.ts` 2. Preview at each width Switchable in the editor, using the same renderer. Files: `src/preview.ts` 3. Verify content is shared Edit text once and check every width. If you have to edit twice, the tree got duplicated. ### Done when - [ ] Rules inherit upward unless overridden - [ ] A content edit applies at every breakpoint - [ ] The export produces matching media queries - [ ] Adding a breakpoint duplicates no nodes ### Watch out - Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. ## M4 · Images that do not ruin the page weight A page with photographs still loads quickly on a phone. ### Steps 1. Generate sizes on import Several widths, plus a modern format alongside a fallback. Files: `src/assets.ts` 2. Emit responsive markup So the browser picks. Serving one size to everyone is the default failure. Files: `src/render.ts` 3. Report page weight Per page, in the editor. Visible while building rather than discovered after launch. Files: `src/ui/Weight.tsx` ### Done when - [ ] A narrow viewport downloads a smaller image - [ ] Images have explicit dimensions and the page does not shift - [ ] Page weight is reported per page - [ ] Oversized imports are downscaled ### Watch out - Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. ## M5 · Static export that works without scripts The published site shows its content with JavaScript disabled. ### Steps 1. Emit semantic markup Map node kinds to real elements. Generic containers everywhere is the tell of a bad builder. Files: `src/export.ts` 2. Put content in the document Not injected. Verify by disabling scripting and reloading. 3. Make output deterministic Two exports identical, so a diff shows real changes. Files: `src/export.test.ts` ### Done when - [ ] All content shows with scripting disabled - [ ] Semantic elements are used rather than generic containers - [ ] No framework runtime is included - [ ] Two exports are identical ### Watch out - A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. ## M6 · Publishing, forms and the things a site needs (production only) The site is live, its forms work, and it can be republished from a version you can diff. ### Steps 1. Deploy static output Any static host with automatic certificates. The output is plain files. Files: `scripts/deploy.sh` 2. Handle form submissions deliberately Somewhere you control, with a decision about retention recorded. Files: `src/forms.ts`, `docs/forms.md` 3. Version control the definition JSON with stable ordering, so a design change is reviewable as a diff. Files: `src/serialise.ts` ### Done when - [ ] The site serves over HTTPS with a valid certificate - [ ] A form submission reaches a destination you control - [ ] A design change produces a readable diff - [ ] Republishing is one command ### Watch out - A form on a static site needs somewhere to post, and wiring it to a third party endpoint sends your visitors' submissions to a service you have not thought about. Decide where submissions go and what happens to them before publishing the form rather than after the first one arrives. ===== OPERATIONS.md ===== # Operations · Framer ## Backup The site definition is a JSON file and belongs in version control, which also gives you history and review. Assets are larger and belong alongside it. ## Restore Check out the definition and assets, then export and compare against the published site. Deterministic output is what makes that comparison meaningful. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Page weight per page over time and export determinism in continuous integration. Weight creeps up as content is added and nobody notices until a visitor on a slow connection does. ## Incident checklist If the published site differs from the preview, the two renderers have diverged, which should be impossible by construction and is the first thing to check. If a page got slow, the weight report will show which asset did it. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] No node stores coordinates - [ ] A content edit is confirmed to apply at every breakpoint - [ ] An exported page is confirmed to show content with scripting disabled - [ ] A narrow viewport is confirmed to download a smaller image - [ ] Two exports are byte-identical ## Launch constraint Do not market omitted Framer capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. The site definition. Version controlled, so a design change is a reviewable diff. VITE_SITE_PATH=./sites/mysite.json # Required. Source images before size generation. VITE_ASSETS_DIR=./assets # Required. Sizes generated per image for responsive markup. VITE_IMAGE_WIDTHS=480,960,1440,1920 # Required. Page weight at which the editor warns. Visible while building rather than after publishing. VITE_PAGE_WEIGHT_WARN_KB=600 # Required. Where static output is written. EXPORT_DIR=./dist # Optional. Where submissions post. Yours, or a service whose retention you have read. FORM_ENDPOINT=https://forms.example.com/submit
# Framer · indie build A visual editor over a layout tree rather than a canvas of positioned boxes, reusable components with overridable content, breakpoints that adjust rules rather than duplicating designs, and a static export of real HTML and CSS that loads fast and works without JavaScript for its content. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with Vite for the editor | one stateful editing surface where a component tree gets in the way | | Model | A layout tree with flow containers, never stored positions | this is the constraint the whole build rests on | | Preview | Real HTML and CSS rendered in an iframe | previewing with the same engine that will render the output is the only way they cannot disagree | | Output | Static HTML and CSS files, with JavaScript optional | a site that needs JavaScript to show its text is slower and more fragile than it needs to be | | Storage | A JSON document per site, plus assets | the site definition is a file you own and can diff | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the editor build and the asset pipeline. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A real site design you want to build** · free - Why: Layout containers are only tested by a real design, which will contain the one arrangement that makes you want an absolute position. Solving that with a container type is the point. - Get it: Take a site you designed or admire and rebuild it. The section that resists is where the container model needs work. - Verify: A real multi-section page is rebuilt without any positioned element - [ ] **Real photographs for the page** · free - Why: Page weight and responsive images are invisible with placeholder graphics and immediately obvious with real photographs. - Get it: Use images you have the rights to, at the sizes they came from the camera. - Verify: A page with photographs reports its weight - [ ] **A static host and a domain** (optional) · often free hosting; roughly 10 to 15 US dollars a year for a domain - Why: Product phase only. The output is plain files and needs somewhere to live with a certificate. - Get it: Any static hosting with automatic certificates, plus a domain from any registrar. - Verify: The site serves over HTTPS - [ ] **A decision about where form submissions go** (optional) · free with your own endpoint - Why: A static site cannot receive a submission by itself, and the easy answer sends your visitors' data to a service you have not evaluated. - Get it: Decide before publishing a form: your own endpoint, or a service whose terms and retention you have read. Record the decision. - Verify: The destination and its retention are documented ## Quick start ```sh npm create vite@latest . -- --template vanilla-ts ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - The visual design polish of the commercial editor. - Animation and interaction design. - The template marketplace. - Managed hosting with a delivery network. - visual design polish - animations - templates - CMS - hosting - preview/publish flow - forms If one of those is essential to you, that is the reason to keep paying for Framer, and the README should say so rather than pretend.
# Build brief · Framer The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a visual site builder producing static output, where responsive means layout rules rather than three fixed designs to replace Framer. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Absolute positioning is the trap that kills visual site builders. Dragging an element to a spot on a canvas produces a design that is correct at one width and wrong at every other, and the usual remedy is to draw the whole page again at two more widths and maintain three designs forever. So the editor never stores a position. Elements go into layout containers with rules, and moving something means changing its place in the tree or its rule. That constraint makes the editor slightly harder to build and makes responsive output a consequence rather than a separate project. ### Stack (fixed, do not substitute) - Runtime: TypeScript with Vite for the editor · one stateful editing surface where a component tree gets in the way - Model: A layout tree with flow containers, never stored positions · this is the constraint the whole build rests on - Preview: Real HTML and CSS rendered in an iframe · previewing with the same engine that will render the output is the only way they cannot disagree - Output: Static HTML and CSS files, with JavaScript optional · a site that needs JavaScript to show its text is slower and more fragile than it needs to be - Storage: A JSON document per site, plus assets · the site definition is a file you own and can diff ### Data model (create this before Phase 1) - sites(id, name, updated_at), pages(id, site_id, path, title, meta_json) - nodes(id, page_id, parent_id, ordinal, kind, props_json, style_rules_json) · kind is stack, grid, text, image, link or component_instance - components(id, site_id, name, root_node_id), instances(node_id, component_id, overrides_json) - breakpoints(id, site_id, name, min_width, ordinal) - style_rules · per node per breakpoint, adjusting the rule rather than redefining the node - assets(id, site_id, path, kind, width, height, bytes) - No node has x and y. A node has a parent, an order and layout rules, which is what makes one design work at every width. ### Phase 1 · A layout tree with no coordinates Build: Stack and grid containers with direction, spacing, padding and alignment. Drag to reparent and reorder rather than to position. Render the tree as real HTML and CSS in an iframe preview. Trap: Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. Done when: No node stores x or y coordinates, verified by inspecting the document. Dragging an element reparents or reorders it and the preview reflects the change. Resizing the preview reflows the page without any breakpoint work. The rendered preview uses the same HTML and CSS the export will produce. Do not build yet: No components, breakpoints or export. ### Phase 2 · Components with overrides Build: Turn a subtree into a component. Instances resolve from the component and store only their overrides. Editing the component updates instances that have not overridden the changed property. Trap: Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. Done when: Editing a component's text updates instances that have not overridden it and leaves overridden ones alone. An override survives an unrelated component change. Deleting a component leaves instances in a visibly broken state naming what is missing. Nesting a component inside another resolves correctly. Do not build yet: No breakpoints or export yet. ### Phase 3 · Breakpoints that adjust rather than duplicate Build: Breakpoints as ordered minimum widths. A node's rules can be overridden per breakpoint, inheriting from the one below where nothing is specified. Never duplicate the tree per breakpoint. Trap: Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. Done when: A node with a rule at one breakpoint inherits it at wider ones unless overridden. Changing content changes it at every breakpoint because there is one tree, verified by editing text and checking all widths. The export produces media queries matching the breakpoints. Adding a breakpoint does not duplicate any node. Do not build yet: No assets or export yet. ### Phase 4 · Images that do not ruin the page weight Build: Import images, generate several sizes, and emit responsive image markup so a phone downloads a phone-sized file. Set explicit dimensions so the page does not shift as images load. Report page weight per page. Trap: Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. Done when: A page with images emits responsive markup and a narrow viewport downloads a smaller file, verified in developer tools. Images have explicit dimensions and the page does not shift as they load. Page weight is reported per page. An oversized import is downscaled with a note. Do not build yet: No export or deployment yet. ### Phase 5 · Static export that works without scripts Build: Export static HTML and CSS per page with real markup: headings as headings, links as links, and content in the document rather than injected. Any interactivity is added on top and degrades. Verify with scripting disabled. Trap: A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. Done when: An exported page shows all its content with scripting disabled. Headings, links and lists use semantic elements rather than generic containers. The export contains no build-time framework runtime. Exporting twice produces identical files. Do not build yet: No deployment or forms yet. ### Phase 6 · Publishing, forms and the things a site needs Build: Deploy the static output to a host with a certificate. Add a form that posts somewhere you control, since a static site cannot handle a submission alone. Keep the site definition in version control so a change is reviewable. Trap: A form on a static site needs somewhere to post, and wiring it to a third party endpoint sends your visitors' submissions to a service you have not thought about. Decide where submissions go and what happens to them before publishing the form rather than after the first one arrives. Done when: The site serves over HTTPS with a valid certificate. A form submission reaches a destination you control and the visitor sees a confirmation. The site definition is in version control and a change produces a readable diff. Republishing is one command. Do not build yet: Nothing after this. ### Out of scope (and why) - The visual design polish of the commercial product, which is years of attention to how the editor feels and is most of why designers pay for it. - Animation and interaction design, which is a substantial product of its own and where this category's real difficulty lives. - The template marketplace, which is content rather than code. - Managed hosting with a content delivery network, which is now a static host you choose and configure. ### README must contain - That no node stores coordinates, and why absolute positioning is refused - That breakpoints override rules on one tree rather than duplicating designs - That the preview uses the same renderer as the export - That exported pages show their content with scripting disabled - Where form submissions go and what happens to them
# Agent instructions · Framer indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: TypeScript with Vite for the editor, A layout tree with flow containers, never stored positions, Real HTML and CSS rendered in an iframe, Static HTML and CSS files, with JavaScript optional, A JSON document per site, plus assets. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. - Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. - Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. - Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. - A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism.
# Build plan · Framer A visual editor over a layout tree rather than a canvas of positioned boxes, reusable components with overridable content, breakpoints that adjust rules rather than duplicating designs, and a static export of real HTML and CSS that loads fast and works without JavaScript for its content. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · A layout tree with no coordinates You can build a page visually and nothing stores a position. ### Steps 1. Model containers and flow Stack and grid with real rules. Direction, gap, padding, alignment. Files: `src/model.ts` ```sh npm create vite@latest . -- --template vanilla-ts ``` 2. Make dragging reparent, not position Drop targets are containers and positions in an order, not points on a canvas. Files: `src/drag.ts` 3. Preview with the export renderer The same code path. Two renderers diverge and the difference always appears after publishing. Files: `src/render.ts` ### Done when - [ ] No node stores coordinates - [ ] Dragging reparents or reorders - [ ] Resizing the preview reflows without breakpoint work - [ ] Preview and export use the same renderer ### Watch out - Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. ## Phase 2 · Components with overrides Changing a component changes every instance, except where an instance deliberately differs. ### Steps 1. Store sparse overrides An instance is a component reference plus the properties it changed. Nothing else. Files: `src/components.ts` 2. Resolve at render Every render, so propagation is automatic rather than a sync step. Files: `src/resolve.ts` 3. Handle a missing component visibly Name what is missing. A silent hole is worse than a visible error. Files: `src/resolve.ts` ### Done when - [ ] A component change propagates to non-overridden instances - [ ] An override survives unrelated changes - [ ] A deleted component leaves a named broken state - [ ] Nested components resolve ### Watch out - Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. ## Phase 3 · Breakpoints that adjust rather than duplicate A narrow screen gets a different layout without a second design to maintain. ### Steps 1. Model breakpoints as rule overrides Per node per breakpoint, inheriting upward. One tree, several rule sets. Files: `src/breakpoints.ts` 2. Preview at each width Switchable in the editor, using the same renderer. Files: `src/preview.ts` 3. Verify content is shared Edit text once and check every width. If you have to edit twice, the tree got duplicated. ### Done when - [ ] Rules inherit upward unless overridden - [ ] A content edit applies at every breakpoint - [ ] The export produces matching media queries - [ ] Adding a breakpoint duplicates no nodes ### Watch out - Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. ## Phase 4 · Images that do not ruin the page weight A page with photographs still loads quickly on a phone. ### Steps 1. Generate sizes on import Several widths, plus a modern format alongside a fallback. Files: `src/assets.ts` 2. Emit responsive markup So the browser picks. Serving one size to everyone is the default failure. Files: `src/render.ts` 3. Report page weight Per page, in the editor. Visible while building rather than discovered after launch. Files: `src/ui/Weight.tsx` ### Done when - [ ] A narrow viewport downloads a smaller image - [ ] Images have explicit dimensions and the page does not shift - [ ] Page weight is reported per page - [ ] Oversized imports are downscaled ### Watch out - Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. ## Phase 5 · Static export that works without scripts The published site shows its content with JavaScript disabled. ### Steps 1. Emit semantic markup Map node kinds to real elements. Generic containers everywhere is the tell of a bad builder. Files: `src/export.ts` 2. Put content in the document Not injected. Verify by disabling scripting and reloading. 3. Make output deterministic Two exports identical, so a diff shows real changes. Files: `src/export.test.ts` ### Done when - [ ] All content shows with scripting disabled - [ ] Semantic elements are used rather than generic containers - [ ] No framework runtime is included - [ ] Two exports are identical ### Watch out - A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. ## Not in this build - The visual design polish of the commercial editor. - Animation and interaction design. - The template marketplace. - Managed hosting with a delivery network. ## After v1, if you want it - A content collection so a blog does not mean duplicating a page per post - Design tokens for colour and spacing, applied by reference across the site - A visual diff between two versions of a page, since the definition is already diffable
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. The site definition. Version controlled, so a design change is a reviewable diff. VITE_SITE_PATH=./sites/mysite.json # Required. Source images before size generation. VITE_ASSETS_DIR=./assets # Required. Sizes generated per image for responsive markup. VITE_IMAGE_WIDTHS=480,960,1440,1920 # Required. Page weight at which the editor warns. Visible while building rather than after publishing. VITE_PAGE_WEIGHT_WARN_KB=600 # Required. Where static output is written. EXPORT_DIR=./dist # Optional. Where submissions post. Yours, or a service whose retention you have read. FORM_ENDPOINT=https://forms.example.com/submit
# Framer · product brief ## Problem You can build a simple AI/site generator and host static pages, but Framer's animation/design editor, CMS, hosting, templates, and publishing polish are real product depth. ## Product outcome A visual builder whose output is one responsive design rather than three, whose pages show their content without scripts, and whose site definition can be reviewed as a diff. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - React/site builder - CMS/storage - static exporter - hosting - domain/DNS - optional LLM API ## Explicit non-goals for v1 - The visual design polish of the commercial editor. - Animation and interaction design. - The template marketplace. - Managed hosting with a delivery network. - visual design polish - animations - templates - CMS - hosting - preview/publish flow - forms ## Success criteria - No node stores coordinates - A content edit is confirmed to apply at every breakpoint - An exported page is confirmed to show content with scripting disabled - A narrow viewport is confirmed to download a smaller image - Two exports are byte-identical
# Build brief · Framer The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a visual site builder producing static output, where responsive means layout rules rather than three fixed designs to replace Framer. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Absolute positioning is the trap that kills visual site builders. Dragging an element to a spot on a canvas produces a design that is correct at one width and wrong at every other, and the usual remedy is to draw the whole page again at two more widths and maintain three designs forever. So the editor never stores a position. Elements go into layout containers with rules, and moving something means changing its place in the tree or its rule. That constraint makes the editor slightly harder to build and makes responsive output a consequence rather than a separate project. ### Stack (fixed, do not substitute) - Runtime: TypeScript with Vite for the editor · one stateful editing surface where a component tree gets in the way - Model: A layout tree with flow containers, never stored positions · this is the constraint the whole build rests on - Preview: Real HTML and CSS rendered in an iframe · previewing with the same engine that will render the output is the only way they cannot disagree - Output: Static HTML and CSS files, with JavaScript optional · a site that needs JavaScript to show its text is slower and more fragile than it needs to be - Storage: A JSON document per site, plus assets · the site definition is a file you own and can diff ### Data model (create this before Phase 1) - sites(id, name, updated_at), pages(id, site_id, path, title, meta_json) - nodes(id, page_id, parent_id, ordinal, kind, props_json, style_rules_json) · kind is stack, grid, text, image, link or component_instance - components(id, site_id, name, root_node_id), instances(node_id, component_id, overrides_json) - breakpoints(id, site_id, name, min_width, ordinal) - style_rules · per node per breakpoint, adjusting the rule rather than redefining the node - assets(id, site_id, path, kind, width, height, bytes) - No node has x and y. A node has a parent, an order and layout rules, which is what makes one design work at every width. ### Phase 1 · A layout tree with no coordinates Build: Stack and grid containers with direction, spacing, padding and alignment. Drag to reparent and reorder rather than to position. Render the tree as real HTML and CSS in an iframe preview. Trap: Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. Done when: No node stores x or y coordinates, verified by inspecting the document. Dragging an element reparents or reorders it and the preview reflects the change. Resizing the preview reflows the page without any breakpoint work. The rendered preview uses the same HTML and CSS the export will produce. Do not build yet: No components, breakpoints or export. ### Phase 2 · Components with overrides Build: Turn a subtree into a component. Instances resolve from the component and store only their overrides. Editing the component updates instances that have not overridden the changed property. Trap: Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. Done when: Editing a component's text updates instances that have not overridden it and leaves overridden ones alone. An override survives an unrelated component change. Deleting a component leaves instances in a visibly broken state naming what is missing. Nesting a component inside another resolves correctly. Do not build yet: No breakpoints or export yet. ### Phase 3 · Breakpoints that adjust rather than duplicate Build: Breakpoints as ordered minimum widths. A node's rules can be overridden per breakpoint, inheriting from the one below where nothing is specified. Never duplicate the tree per breakpoint. Trap: Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. Done when: A node with a rule at one breakpoint inherits it at wider ones unless overridden. Changing content changes it at every breakpoint because there is one tree, verified by editing text and checking all widths. The export produces media queries matching the breakpoints. Adding a breakpoint does not duplicate any node. Do not build yet: No assets or export yet. ### Phase 4 · Images that do not ruin the page weight Build: Import images, generate several sizes, and emit responsive image markup so a phone downloads a phone-sized file. Set explicit dimensions so the page does not shift as images load. Report page weight per page. Trap: Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. Done when: A page with images emits responsive markup and a narrow viewport downloads a smaller file, verified in developer tools. Images have explicit dimensions and the page does not shift as they load. Page weight is reported per page. An oversized import is downscaled with a note. Do not build yet: No export or deployment yet. ### Phase 5 · Static export that works without scripts Build: Export static HTML and CSS per page with real markup: headings as headings, links as links, and content in the document rather than injected. Any interactivity is added on top and degrades. Verify with scripting disabled. Trap: A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. Done when: An exported page shows all its content with scripting disabled. Headings, links and lists use semantic elements rather than generic containers. The export contains no build-time framework runtime. Exporting twice produces identical files. Do not build yet: No deployment or forms yet. ### Phase 6 · Publishing, forms and the things a site needs Build: Deploy the static output to a host with a certificate. Add a form that posts somewhere you control, since a static site cannot handle a submission alone. Keep the site definition in version control so a change is reviewable. Trap: A form on a static site needs somewhere to post, and wiring it to a third party endpoint sends your visitors' submissions to a service you have not thought about. Decide where submissions go and what happens to them before publishing the form rather than after the first one arrives. Done when: The site serves over HTTPS with a valid certificate. A form submission reaches a destination you control and the visitor sees a confirmation. The site definition is in version control and a change produces a readable diff. Republishing is one command. Do not build yet: Nothing after this. ### Out of scope (and why) - The visual design polish of the commercial product, which is years of attention to how the editor feels and is most of why designers pay for it. - Animation and interaction design, which is a substantial product of its own and where this category's real difficulty lives. - The template marketplace, which is content rather than code. - Managed hosting with a content delivery network, which is now a static host you choose and configure. ### README must contain - That no node stores coordinates, and why absolute positioning is refused - That breakpoints override rules on one tree rather than duplicating designs - That the preview uses the same renderer as the export - That exported pages show their content with scripting disabled - Where form submissions go and what happens to them
# Architecture · Framer ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with Vite for the editor | one stateful editing surface where a component tree gets in the way | | Model | A layout tree with flow containers, never stored positions | this is the constraint the whole build rests on | | Preview | Real HTML and CSS rendered in an iframe | previewing with the same engine that will render the output is the only way they cannot disagree | | Output | Static HTML and CSS files, with JavaScript optional | a site that needs JavaScript to show its text is slower and more fragile than it needs to be | | Storage | A JSON document per site, plus assets | the site definition is a file you own and can diff | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Layout tree | Containers, order and rules. | Not swappable for a positioned canvas. It is the constraint that produces responsive output for free. | | Components | Reuse with sparse overrides. | Any implementation resolving at render. Copies break propagation. | | Breakpoints | Rule overrides on one tree. | Any override model, provided the tree is never duplicated. | | Assets | Sizes and responsive markup. | Any pipeline. Reporting weight in the editor is what keeps pages fast. | | Export | Semantic static output. | Any renderer, provided it is the same one the preview uses. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `VITE_SITE_PATH` · required · The site definition. Version controlled, so a design change is a reviewable diff. - `VITE_ASSETS_DIR` · required · Source images before size generation. - `VITE_IMAGE_WIDTHS` · required · Sizes generated per image for responsive markup. - `VITE_PAGE_WEIGHT_WARN_KB` · required · Page weight at which the editor warns. Visible while building rather than after publishing. - `EXPORT_DIR` · required · Where static output is written. - `FORM_ENDPOINT` · optional · Where submissions post. Yours, or a service whose retention you have read. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Framer product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: TypeScript with Vite for the editor, A layout tree with flow containers, never stored positions, Real HTML and CSS rendered in an iframe, Static HTML and CSS files, with JavaScript optional, A JSON document per site, plus assets. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. - Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. - Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. - Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. - A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. - A form on a static site needs somewhere to post, and wiring it to a third party endpoint sends your visitors' submissions to a service you have not thought about. Decide where submissions go and what happens to them before publishing the form rather than after the first one arrives.
# Delivery milestones · Framer Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · A layout tree with no coordinates You can build a page visually and nothing stores a position. ### Steps 1. Model containers and flow Stack and grid with real rules. Direction, gap, padding, alignment. Files: `src/model.ts` ```sh npm create vite@latest . -- --template vanilla-ts ``` 2. Make dragging reparent, not position Drop targets are containers and positions in an order, not points on a canvas. Files: `src/drag.ts` 3. Preview with the export renderer The same code path. Two renderers diverge and the difference always appears after publishing. Files: `src/render.ts` ### Done when - [ ] No node stores coordinates - [ ] Dragging reparents or reorders - [ ] Resizing the preview reflows without breakpoint work - [ ] Preview and export use the same renderer ### Watch out - Allowing an escape hatch of absolute positioning for one tricky element means every subsequent tricky element uses it too, and within a month the site is a canvas again. If a layout genuinely needs it, that is a missing container type rather than a reason to store coordinates. ## M2 · Components with overrides Changing a component changes every instance, except where an instance deliberately differs. ### Steps 1. Store sparse overrides An instance is a component reference plus the properties it changed. Nothing else. Files: `src/components.ts` 2. Resolve at render Every render, so propagation is automatic rather than a sync step. Files: `src/resolve.ts` 3. Handle a missing component visibly Name what is missing. A silent hole is worse than a visible error. Files: `src/resolve.ts` ### Done when - [ ] A component change propagates to non-overridden instances - [ ] An override survives unrelated changes - [ ] A deleted component leaves a named broken state - [ ] Nested components resolve ### Watch out - Storing an instance as a copy with a link back makes a component change ambiguous, because you cannot tell which differences were deliberate and which are stale. Store only overrides and resolve at render, which is what makes propagation automatic. ## M3 · Breakpoints that adjust rather than duplicate A narrow screen gets a different layout without a second design to maintain. ### Steps 1. Model breakpoints as rule overrides Per node per breakpoint, inheriting upward. One tree, several rule sets. Files: `src/breakpoints.ts` 2. Preview at each width Switchable in the editor, using the same renderer. Files: `src/preview.ts` 3. Verify content is shared Edit text once and check every width. If you have to edit twice, the tree got duplicated. ### Done when - [ ] Rules inherit upward unless overridden - [ ] A content edit applies at every breakpoint - [ ] The export produces matching media queries - [ ] Adding a breakpoint duplicates no nodes ### Watch out - Duplicating the node tree per breakpoint means every content change has to be made three times and they drift within a week, which is the exact problem responsive design was invented to solve. One tree, rules overridden per breakpoint, inherited upward. ## M4 · Images that do not ruin the page weight A page with photographs still loads quickly on a phone. ### Steps 1. Generate sizes on import Several widths, plus a modern format alongside a fallback. Files: `src/assets.ts` 2. Emit responsive markup So the browser picks. Serving one size to everyone is the default failure. Files: `src/render.ts` 3. Report page weight Per page, in the editor. Visible while building rather than discovered after launch. Files: `src/ui/Weight.tsx` ### Done when - [ ] A narrow viewport downloads a smaller image - [ ] Images have explicit dimensions and the page does not shift - [ ] Page weight is reported per page - [ ] Oversized imports are downscaled ### Watch out - Serving one large image to every device is the single biggest cause of slow pages built this way, and it is invisible on a fast connection. Generate sizes, emit the markup that lets the browser choose, and report the total weight so the problem is visible while you build rather than after. ## M5 · Static export that works without scripts The published site shows its content with JavaScript disabled. ### Steps 1. Emit semantic markup Map node kinds to real elements. Generic containers everywhere is the tell of a bad builder. Files: `src/export.ts` 2. Put content in the document Not injected. Verify by disabling scripting and reloading. 3. Make output deterministic Two exports identical, so a diff shows real changes. Files: `src/export.test.ts` ### Done when - [ ] All content shows with scripting disabled - [ ] Semantic elements are used rather than generic containers - [ ] No framework runtime is included - [ ] Two exports are identical ### Watch out - A visual builder that emits a page of generic containers filled by JavaScript produces a site that is slower, less accessible and invisible to anything that does not run scripts. Emit semantic markup with the content present, and treat interactivity as an enhancement rather than the delivery mechanism. ## M6 · Publishing, forms and the things a site needs (production only) The site is live, its forms work, and it can be republished from a version you can diff. ### Steps 1. Deploy static output Any static host with automatic certificates. The output is plain files. Files: `scripts/deploy.sh` 2. Handle form submissions deliberately Somewhere you control, with a decision about retention recorded. Files: `src/forms.ts`, `docs/forms.md` 3. Version control the definition JSON with stable ordering, so a design change is reviewable as a diff. Files: `src/serialise.ts` ### Done when - [ ] The site serves over HTTPS with a valid certificate - [ ] A form submission reaches a destination you control - [ ] A design change produces a readable diff - [ ] Republishing is one command ### Watch out - A form on a static site needs somewhere to post, and wiring it to a third party endpoint sends your visitors' submissions to a service you have not thought about. Decide where submissions go and what happens to them before publishing the form rather than after the first one arrives.
# Operations · Framer ## Backup The site definition is a JSON file and belongs in version control, which also gives you history and review. Assets are larger and belong alongside it. ## Restore Check out the definition and assets, then export and compare against the published site. Deterministic output is what makes that comparison meaningful. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Page weight per page over time and export determinism in continuous integration. Weight creeps up as content is added and nobody notices until a visitor on a slow connection does. ## Incident checklist If the published site differs from the preview, the two renderers have diverged, which should be impossible by construction and is the first thing to check. If a page got slow, the weight report will show which asset did it. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] No node stores coordinates - [ ] A content edit is confirmed to apply at every breakpoint - [ ] An exported page is confirmed to show content with scripting disabled - [ ] A narrow viewport is confirmed to download a smaller image - [ ] Two exports are byte-identical ## Launch constraint Do not market omitted Framer capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. The site definition. Version controlled, so a design change is a reviewable diff. VITE_SITE_PATH=./sites/mysite.json # Required. Source images before size generation. VITE_ASSETS_DIR=./assets # Required. Sizes generated per image for responsive markup. VITE_IMAGE_WIDTHS=480,960,1440,1920 # Required. Page weight at which the editor warns. Visible while building rather than after publishing. VITE_PAGE_WEIGHT_WARN_KB=600 # Required. Where static output is written. EXPORT_DIR=./dist # Optional. Where submissions post. Yours, or a service whose retention you have read. FORM_ENDPOINT=https://forms.example.com/submit
$ choose a build depth, inspect the files, then open the complete pack in your agent
They pay because polished landing pages ship quickly with minimal design-engineering friction.
xvisual design polish
xanimations
xtemplates
xCMS
xhosting
xpreview/publish flow
xforms
Don't feel like building it? These folks already made it free.
no votes, no pay-to-list · just what's real
Framer pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| free | $0/workspace | $0/workspace | Framer subdomain; 1 GB bandwidth; 10 CMS collections; 1,000 pages; 5 MB file uploads; 1 free locale; up to 3 editors; 500 AI credits to try |
| basic | — | $10 | 30 pages; 2 CMS collections; 1,000 CMS items; 50 GB bandwidth/month; 1,000 AI credits/month; up to 10 editors |
| pro | — | $30 | 150 pages; 10 CMS collections; 2,500 CMS items; 100 GB bandwidth/month; 3,000 AI credits/month; up to 10 editors |
| enterprise | custom | — | Custom limits; unlimited editors; enterprise security, uptime guarantee, SCIM and SSO |
free tierFramer subdomain; 1 GB bandwidth; 10 CMS collections; 1,000 pages; 5 MB upload limit; 1 locale; up to 3 editors; 500 trial AI credits
billingmonthly + annual for Basic and Pro; annual billed upfront, while added editors and usage on annual plans are billed monthly
hidden costsExtra design editors are $20/month each and content editors $10/month each; locales cost $20/month each; Pro expansion is metered ($20 per extra 100 pages, $40 per 10 CMS collections, $20 per 10,000 CMS items, $40 per 100 GB bandwidth), Convert starts at $50 per 500,000 events, and Advanced Hosting costs $200/month.
verified 2026-08-14 · source ↗
Vibecode Framer
Kinda. The core of Framer is buildable in a weekend with the prompt on this page, but there are real gaps: visual design polish, animations. Read the honest list above before committing.
How much does Framer cost?
Framer costs about $10/month (Basic, checked 2026-07-30), which is $120 per year.
What do I lose by replacing Framer?
Honestly: visual design polish; animations; templates; CMS; hosting; preview/publish flow; forms. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Framer?
Yes: Plasmic (A hosted visual builder with CMS, interactions, Figma import and a custom domain free; the badge remains.) The prompt is for when you want it exactly your way.