Vibecode Sleek
track this build4 phases, 12 steps, beginner friendly0%One screen is already a yes, and you do not need this site for it: paste "design me a settings screen for an iOS app, Tailwind, 402x874" into the agent you already pay for and you get something decent. What Sleek sells is everything that makes twelve screens look like the same app, and most of that turns out to be prompt discipline rather than product. Decide the design system once, paste it verbatim into every later call, and hand the model the mobile conventions it does not apply unprompted: safe areas, 44px tap targets, native navigation instead of a website cropped to phone width. Two gaps survive that. There is no canvas, so every change is a re-prompt of a whole file rather than a click, and one call to one model is a rougher first draft than a tuned chain of them, which you pay for in your own iteration time. The Figma handoff is not one of the gaps: Sleek's own team ships the converter as MIT on npm, so the DIY build gets editable layers from one install.
You are building a lean indie version of Sleek. 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 ===== # Sleek · indie build A prompt-to-screen tool for mobile apps where one screen is easy and the product is everything that makes twelve screens look like one app: a design system decided once and never regenerated, mobile conventions the model does not apply unprompted and so must be measured rather than eyeballed, edits that leave the rest of a file alone, and a Figma export that only works from a real click. Estimated effort: **weekend**. 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 | Next.js with the App Router, screens as files on disk, no database | a folder of HTML files needs no schema and every edit is a diff | | Model | One provider behind one streaming route handler, model name in the environment | the product is prompt discipline rather than a model | | Screens | One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame | a file per screen makes history, diffing and export trivial | | Consistency | design-system.json and _nav.html generated once and pasted verbatim into every later call | this is what stops screen nine drifting from screen one | | Export | Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers | the converter Sleek itself ships is on npm under MIT | ## 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 application, the checks and the export. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **An API key for a model that accepts images** · per token, roughly one long call per screen and per edit - Why: The sketch input and every screen are calls to it, and the cost per screen is what the ceiling protects you from. - Get it: A provider account with per-token billing; note the price of a long output before generating ten screens. - Verify: One screen generates and its cost appears in calls.jsonl - [ ] **Playwright with Chromium** · free - Why: It measures insets and tap targets and renders the exports; without it every check is by eye. - Get it: npm install playwright, then npx playwright install chromium. - Verify: A headless page opens and reports an element's bounding box - [ ] **The target device, with its logical size and insets measured** · free - Why: Every screen is generated at one size and checked against one set of insets, and a guessed inset means content under the status bar on the real phone. - Get it: Pick the device, take its logical size from the pixel resolution divided by the scale factor, and measure the safe-area insets on a simulator. - Verify: project.json holds the width, height and both insets with a note on where they came from - [ ] **A Figma account to test the paste** (optional) · free - Why: Editable layers are the export that matters and the only way to confirm them is to paste. - Get it: A free Figma account and an empty file. - Verify: A pasted screen appears as layers rather than as an image ## Quick start ```sh npx playwright install chromium ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - A click-to-edit canvas. - Share links, accounts and hosting. - A chain of tuned model calls per screen. - Sketch recognition beyond passing the image to the model. - clicking an element to change it instead of re-prompting the whole file - a tuned multi-step pipeline · you get one prompt on one model, and you close the gap in your own iteration time - sketch input · photograph a hand-drawn screen and get it back as a design - share links, project history, and team seats If one of those is essential to you, that is the reason to keep paying for Sleek, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Sleek 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 multi-screen mobile app designer that keeps screen nine looking like screen one to replace Sleek. 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. ### Stack (fixed, do not substitute) - Runtime: Next.js with the App Router, screens as files on disk, no database · a folder of HTML files needs no schema and every edit is a diff - Model: One provider behind one streaming route handler, model name in the environment · the product is prompt discipline rather than a model - Screens: One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame · a file per screen makes history, diffing and export trivial - Consistency: design-system.json and _nav.html generated once and pasted verbatim into every later call · this is what stops screen nine drifting from screen one - Export: Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers · the converter Sleek itself ships is on npm under MIT ### Data model (create this before Phase 1) - project.json(name, description, device, width_pt, height_pt, safe_top_pt, safe_bottom_pt, created_at) · the target device, measured rather than guessed - design-system.json(colours[5], fonts[2], type_scale[6], radius, spacing_unit, icon_set) · written once, then read only - _nav.html · the shared navigation, generated with the design system and byte-identical in every screen - screens/<name>.html · one standalone file per screen, the only thing the model rewrites - screens/.history/<name>/<timestamp>.html · every version ever produced, never pruned automatically - calls.jsonl(at, screen, purpose, model, tokens_in, tokens_out, cost_minor) · one line per model call, so the bill is visible before it arrives - The design system is generated once and copied into every prompt verbatim, because letting later calls improve it is how the twelfth screen stops looking like the first. ### Phase 1 · The screen list and the design system, decided once Build: Take a description of the app, or a photographed sketch, and answer first with six to ten screen names with one line of purpose each, which the person edits before anything is built. The same call writes design-system.json and _nav.html. Lock both: regenerating is an explicit action that versions the old files, never a side effect of a later call. Trap: Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. Done when: The screen list is editable before any screen is generated. design-system.json holds five colours, two fonts, a six-step type scale, a radius, a spacing unit and an icon set, and _nav.html exists. Generating a screen leaves both files byte-identical, tested by hashing them before and after. Every model call appends a line to calls.jsonl with tokens and cost. Do not build yet: No screens, edits, gallery or export. ### Phase 2 · Screens that obey the phone, checked by machine Build: Generate each screen as one standalone HTML file at the device's logical size, with the safe-area insets as CSS variables, the design system and nav pasted verbatim, and the mobile conventions spelled out in the prompt. Then open the file headlessly and measure: content inside the insets, every interactive element at least the platform's default control size, the nav byte-identical. A screen that fails is regenerated with the failures named. Trap: Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. Done when: Every screen renders at the target logical size with all content inside the safe-area insets, measured with Playwright. Every interactive element is at least 44 by 44 points, the iOS default control size in Apple's guidelines and the enhanced target size in WCAG, or the screen is flagged with the offending element named. The nav block in every screen is byte-identical to _nav.html. A screen with a remote image loads it through the image proxy. Do not build yet: No edits, gallery or export yet. ### Phase 3 · Edits that leave the rest alone Build: An edit re-sends the current file plus the instruction and overwrites the file in place, after saving the previous version to history. Show a diff of what changed before it is accepted, and make revert one action. Trap: An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. Done when: Editing one component leaves every other line of the file unchanged, asserted by diffing a fixture before and after. Every version is in history with a timestamp and revert restores it byte-identically. The design system and nav pasted into the edit prompt are still verbatim. A diff that touches far more than the instruction implies is shown before it is accepted rather than applied silently. Do not build yet: No gallery, export or Figma yet. ### Phase 4 · The gallery, and pixels at device resolution Build: A gallery of every screen in a phone frame, side by side, with an edit box under each. An export command renders each file in its own page with Playwright at device scale three into exports/, plus a contact sheet. Remote images go through a small proxy so they render. Trap: Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. Done when: Export produces one image per screen whose pixel dimensions are three times the logical size, asserted by reading the files. A contact sheet of all screens is produced. A screen that fails to render is reported by name rather than skipped. Web fonts are loaded before the screenshot is taken. Do not build yet: No Figma export or cost ceiling yet. ### Phase 5 · Editable layers, a ceiling, and what this is not Build: A copy-all-to-Figma button using @figit/dom-to-figma: one frame per screen from the iframe's document body, converted, then written to the clipboard from the click handler. A per-project cost ceiling that refuses further calls. A README that states there is no canvas and every change is a re-prompt. Trap: Writing to the clipboard after an awaited conversion that started from a click can fall outside the user activation window, and Safari and Firefox refuse the write silently. Hand the pending conversion to the clipboard item as a promise inside the click handler, and show a failure message rather than assuming the paste will work. Done when: Pasting into Figma yields editable layers for every screen, checked by hand once, and the clipboard payload is asserted non-empty in a test. A copy attempted outside a user gesture shows a visible failure. A project whose logged cost reaches the ceiling refuses further model calls with the total shown. The README states that there is no canvas and that every change is a re-prompt of a whole file. Do not build yet: Nothing after this. ### Out of scope (and why) - A click-to-edit canvas, which is the product's real work and would take longer than everything else here combined; a diff after a re-prompt is the honest substitute. - Share links, accounts and hosting, since the tool runs on your machine and the screens are files. - A chain of tuned model calls per screen, which is what months of the vendor's iteration bought and which one prompt on one model approximates roughly. - Sketch recognition beyond handing the photograph to the model, which is a research problem rather than a weekend. ### README must contain - That the design system and nav are generated once and pasted verbatim, and that regenerating them is destructive and versioned - That insets and tap targets are measured in a headless browser, because the eye is what the model fooled - That every edit works from the current file and is a diff with history, never a regeneration from the description - That the Figma copy must be fired from a real click and fails visibly otherwise - That every screen is a model call and the project ceiling refuses rather than warns ===== AGENTS.md ===== # Agent instructions · Sleek indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Next.js with the App Router, screens as files on disk, no database, One provider behind one streaming route handler, model name in the environment, One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame, design-system.json and _nav.html generated once and pasted verbatim into every later call, Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers. 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 - Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. - Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. - An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. - Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. ===== BUILD_PLAN.md ===== # Build plan · Sleek A prompt-to-screen tool for mobile apps where one screen is easy and the product is everything that makes twelve screens look like one app: a design system decided once and never regenerated, mobile conventions the model does not apply unprompted and so must be measured rather than eyeballed, edits that leave the rest of a file alone, and a Figma export that only works from a real click. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · The screen list and the design system, decided once One call plans the app and fixes its design system, and nothing regenerates that later. ### Steps 1. Plan the screen list before building anything Editing a name is free and regenerating a screen is not. Files: `app/api/plan/route.ts`, `lib/prompts/plan.ts` 2. Write the design system once and lock it Every later prompt reads it; nothing later writes it. Files: `lib/design-system.ts`, `tests/design-system-locked.test.ts` 3. Log every call with its cost The bill should be visible before it arrives. Files: `lib/calls.ts` ### Done when - [ ] The screen list is editable before generation - [ ] The design system has all six parts and the nav exists - [ ] Generating a screen leaves both files byte-identical - [ ] Every call is logged with tokens and cost ### Watch out - Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. ## Phase 2 · Screens that obey the phone, checked by machine Each screen is a standalone file that respects insets, tap targets and native navigation. ### Steps 1. Give the model the conventions it will not apply alone Safe areas, control sizes, native navigation, no hover states. Files: `lib/prompts/screen.ts`, `app/api/screen/route.ts` 2. Measure insets and tap targets mechanically Bounding boxes in a headless browser, not a glance at the gallery. Files: `scripts/check-screens.ts`, `tests/tap-targets.test.ts` ```sh npx playwright install chromium ``` 3. Keep the nav byte-identical One shared file, compared on every generation. Files: `lib/screens.ts` ### Done when - [ ] Content sits inside the insets on every screen - [ ] Every tappable element is at least 44 by 44 points or flagged - [ ] The nav is byte-identical across screens - [ ] Remote images load through the proxy ### Watch out - Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. ## Phase 3 · Edits that leave the rest alone An edit changes what was asked and nothing else, and every version is kept. ### Steps 1. Edit from the current file, never from the description The description is six edits out of date. Files: `app/api/edit/route.ts`, `lib/prompts/edit.ts` 2. Version before overwriting History is the undo, so it must exist before the write. Files: `lib/history.ts` 3. Show the diff before accepting A whole-file rewrite should be visible, not discovered. Files: `components/EditDiff.tsx`, `tests/edit-locality.test.ts` ### Done when - [ ] An edit leaves unrelated lines unchanged - [ ] Every version is kept and revert is byte-identical - [ ] The pasted design system stays verbatim - [ ] Large diffs are shown before they apply ### Watch out - An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. ## Phase 4 · The gallery, and pixels at device resolution All screens are reviewed side by side and exported at the density of the phone. ### Steps 1. Build the gallery with an edit box per screen Review and the next instruction in one place. Files: `components/Gallery.tsx`, `components/PhoneFrame.tsx` 2. Render each file alone at device scale Not the gallery page. Assert the dimensions. Files: `scripts/export.ts`, `tests/export-dimensions.test.ts` 3. Proxy remote images CORS failures are otherwise blank rectangles in the export. Files: `app/api/image/route.ts` ### Done when - [ ] Each export is three times the logical size - [ ] A contact sheet exists - [ ] A failed render is reported by name - [ ] Fonts load before screenshots ### Watch out - Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. ## Not in this build - A click-to-edit canvas. - Share links, accounts and hosting. - A chain of tuned model calls per screen. - Sketch recognition beyond passing the image to the model. ## After v1, if you want it - A regeneration preview that shows what a new design system would change before it does - Component extraction, so a card used on four screens becomes one snippet pasted into each - Dark mode as a second design system applied to the same screens ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Billed per token on every screen and every edit. MODEL_API_KEY=sk-...redacted # Required. Copied from the provider's model list on the day you set up; it changes. MODEL_NAME=the provider's current model id # Required. One file per screen, with .history beside it. Back this folder up. SCREENS_DIR=./public/screens # Required. Logical width and height of the target device. The example is a 2622 by 1206 pixel display at scale three; check yours. DEVICE_SIZE_PT=402x874 # Required. Top and bottom safe-area insets, measured on a simulator for your device. Do not copy the example. SAFE_INSETS_PT=62,34 # Required. The iOS default control size in Apple's guidelines. The check flags anything smaller. MIN_CONTROL_PT=44 # Required. Device scale factor for screenshots. Output dimensions are asserted against it. EXPORT_SCALE=3 # Required. Refuses further calls at this total. A warning is read as permission. PROJECT_COST_CEILING_MINOR=2000
You are building a lean indie version of Sleek. 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 ===== # Sleek · indie build A prompt-to-screen tool for mobile apps where one screen is easy and the product is everything that makes twelve screens look like one app: a design system decided once and never regenerated, mobile conventions the model does not apply unprompted and so must be measured rather than eyeballed, edits that leave the rest of a file alone, and a Figma export that only works from a real click. Estimated effort: **weekend**. 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 | Next.js with the App Router, screens as files on disk, no database | a folder of HTML files needs no schema and every edit is a diff | | Model | One provider behind one streaming route handler, model name in the environment | the product is prompt discipline rather than a model | | Screens | One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame | a file per screen makes history, diffing and export trivial | | Consistency | design-system.json and _nav.html generated once and pasted verbatim into every later call | this is what stops screen nine drifting from screen one | | Export | Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers | the converter Sleek itself ships is on npm under MIT | ## 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 application, the checks and the export. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **An API key for a model that accepts images** · per token, roughly one long call per screen and per edit - Why: The sketch input and every screen are calls to it, and the cost per screen is what the ceiling protects you from. - Get it: A provider account with per-token billing; note the price of a long output before generating ten screens. - Verify: One screen generates and its cost appears in calls.jsonl - [ ] **Playwright with Chromium** · free - Why: It measures insets and tap targets and renders the exports; without it every check is by eye. - Get it: npm install playwright, then npx playwright install chromium. - Verify: A headless page opens and reports an element's bounding box - [ ] **The target device, with its logical size and insets measured** · free - Why: Every screen is generated at one size and checked against one set of insets, and a guessed inset means content under the status bar on the real phone. - Get it: Pick the device, take its logical size from the pixel resolution divided by the scale factor, and measure the safe-area insets on a simulator. - Verify: project.json holds the width, height and both insets with a note on where they came from - [ ] **A Figma account to test the paste** (optional) · free - Why: Editable layers are the export that matters and the only way to confirm them is to paste. - Get it: A free Figma account and an empty file. - Verify: A pasted screen appears as layers rather than as an image ## Quick start ```sh npx playwright install chromium ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - A click-to-edit canvas. - Share links, accounts and hosting. - A chain of tuned model calls per screen. - Sketch recognition beyond passing the image to the model. - clicking an element to change it instead of re-prompting the whole file - a tuned multi-step pipeline · you get one prompt on one model, and you close the gap in your own iteration time - sketch input · photograph a hand-drawn screen and get it back as a design - share links, project history, and team seats If one of those is essential to you, that is the reason to keep paying for Sleek, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Sleek 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 multi-screen mobile app designer that keeps screen nine looking like screen one to replace Sleek. 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. ### Stack (fixed, do not substitute) - Runtime: Next.js with the App Router, screens as files on disk, no database · a folder of HTML files needs no schema and every edit is a diff - Model: One provider behind one streaming route handler, model name in the environment · the product is prompt discipline rather than a model - Screens: One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame · a file per screen makes history, diffing and export trivial - Consistency: design-system.json and _nav.html generated once and pasted verbatim into every later call · this is what stops screen nine drifting from screen one - Export: Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers · the converter Sleek itself ships is on npm under MIT ### Data model (create this before Phase 1) - project.json(name, description, device, width_pt, height_pt, safe_top_pt, safe_bottom_pt, created_at) · the target device, measured rather than guessed - design-system.json(colours[5], fonts[2], type_scale[6], radius, spacing_unit, icon_set) · written once, then read only - _nav.html · the shared navigation, generated with the design system and byte-identical in every screen - screens/<name>.html · one standalone file per screen, the only thing the model rewrites - screens/.history/<name>/<timestamp>.html · every version ever produced, never pruned automatically - calls.jsonl(at, screen, purpose, model, tokens_in, tokens_out, cost_minor) · one line per model call, so the bill is visible before it arrives - The design system is generated once and copied into every prompt verbatim, because letting later calls improve it is how the twelfth screen stops looking like the first. ### Phase 1 · The screen list and the design system, decided once Build: Take a description of the app, or a photographed sketch, and answer first with six to ten screen names with one line of purpose each, which the person edits before anything is built. The same call writes design-system.json and _nav.html. Lock both: regenerating is an explicit action that versions the old files, never a side effect of a later call. Trap: Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. Done when: The screen list is editable before any screen is generated. design-system.json holds five colours, two fonts, a six-step type scale, a radius, a spacing unit and an icon set, and _nav.html exists. Generating a screen leaves both files byte-identical, tested by hashing them before and after. Every model call appends a line to calls.jsonl with tokens and cost. Do not build yet: No screens, edits, gallery or export. ### Phase 2 · Screens that obey the phone, checked by machine Build: Generate each screen as one standalone HTML file at the device's logical size, with the safe-area insets as CSS variables, the design system and nav pasted verbatim, and the mobile conventions spelled out in the prompt. Then open the file headlessly and measure: content inside the insets, every interactive element at least the platform's default control size, the nav byte-identical. A screen that fails is regenerated with the failures named. Trap: Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. Done when: Every screen renders at the target logical size with all content inside the safe-area insets, measured with Playwright. Every interactive element is at least 44 by 44 points, the iOS default control size in Apple's guidelines and the enhanced target size in WCAG, or the screen is flagged with the offending element named. The nav block in every screen is byte-identical to _nav.html. A screen with a remote image loads it through the image proxy. Do not build yet: No edits, gallery or export yet. ### Phase 3 · Edits that leave the rest alone Build: An edit re-sends the current file plus the instruction and overwrites the file in place, after saving the previous version to history. Show a diff of what changed before it is accepted, and make revert one action. Trap: An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. Done when: Editing one component leaves every other line of the file unchanged, asserted by diffing a fixture before and after. Every version is in history with a timestamp and revert restores it byte-identically. The design system and nav pasted into the edit prompt are still verbatim. A diff that touches far more than the instruction implies is shown before it is accepted rather than applied silently. Do not build yet: No gallery, export or Figma yet. ### Phase 4 · The gallery, and pixels at device resolution Build: A gallery of every screen in a phone frame, side by side, with an edit box under each. An export command renders each file in its own page with Playwright at device scale three into exports/, plus a contact sheet. Remote images go through a small proxy so they render. Trap: Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. Done when: Export produces one image per screen whose pixel dimensions are three times the logical size, asserted by reading the files. A contact sheet of all screens is produced. A screen that fails to render is reported by name rather than skipped. Web fonts are loaded before the screenshot is taken. Do not build yet: No Figma export or cost ceiling yet. ### Phase 5 · Editable layers, a ceiling, and what this is not Build: A copy-all-to-Figma button using @figit/dom-to-figma: one frame per screen from the iframe's document body, converted, then written to the clipboard from the click handler. A per-project cost ceiling that refuses further calls. A README that states there is no canvas and every change is a re-prompt. Trap: Writing to the clipboard after an awaited conversion that started from a click can fall outside the user activation window, and Safari and Firefox refuse the write silently. Hand the pending conversion to the clipboard item as a promise inside the click handler, and show a failure message rather than assuming the paste will work. Done when: Pasting into Figma yields editable layers for every screen, checked by hand once, and the clipboard payload is asserted non-empty in a test. A copy attempted outside a user gesture shows a visible failure. A project whose logged cost reaches the ceiling refuses further model calls with the total shown. The README states that there is no canvas and that every change is a re-prompt of a whole file. Do not build yet: Nothing after this. ### Out of scope (and why) - A click-to-edit canvas, which is the product's real work and would take longer than everything else here combined; a diff after a re-prompt is the honest substitute. - Share links, accounts and hosting, since the tool runs on your machine and the screens are files. - A chain of tuned model calls per screen, which is what months of the vendor's iteration bought and which one prompt on one model approximates roughly. - Sketch recognition beyond handing the photograph to the model, which is a research problem rather than a weekend. ### README must contain - That the design system and nav are generated once and pasted verbatim, and that regenerating them is destructive and versioned - That insets and tap targets are measured in a headless browser, because the eye is what the model fooled - That every edit works from the current file and is a diff with history, never a regeneration from the description - That the Figma copy must be fired from a real click and fails visibly otherwise - That every screen is a model call and the project ceiling refuses rather than warns ===== AGENTS.md ===== # Agent instructions · Sleek indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Next.js with the App Router, screens as files on disk, no database, One provider behind one streaming route handler, model name in the environment, One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame, design-system.json and _nav.html generated once and pasted verbatim into every later call, Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers. 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 - Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. - Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. - An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. - Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. ===== BUILD_PLAN.md ===== # Build plan · Sleek A prompt-to-screen tool for mobile apps where one screen is easy and the product is everything that makes twelve screens look like one app: a design system decided once and never regenerated, mobile conventions the model does not apply unprompted and so must be measured rather than eyeballed, edits that leave the rest of a file alone, and a Figma export that only works from a real click. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · The screen list and the design system, decided once One call plans the app and fixes its design system, and nothing regenerates that later. ### Steps 1. Plan the screen list before building anything Editing a name is free and regenerating a screen is not. Files: `app/api/plan/route.ts`, `lib/prompts/plan.ts` 2. Write the design system once and lock it Every later prompt reads it; nothing later writes it. Files: `lib/design-system.ts`, `tests/design-system-locked.test.ts` 3. Log every call with its cost The bill should be visible before it arrives. Files: `lib/calls.ts` ### Done when - [ ] The screen list is editable before generation - [ ] The design system has all six parts and the nav exists - [ ] Generating a screen leaves both files byte-identical - [ ] Every call is logged with tokens and cost ### Watch out - Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. ## Phase 2 · Screens that obey the phone, checked by machine Each screen is a standalone file that respects insets, tap targets and native navigation. ### Steps 1. Give the model the conventions it will not apply alone Safe areas, control sizes, native navigation, no hover states. Files: `lib/prompts/screen.ts`, `app/api/screen/route.ts` 2. Measure insets and tap targets mechanically Bounding boxes in a headless browser, not a glance at the gallery. Files: `scripts/check-screens.ts`, `tests/tap-targets.test.ts` ```sh npx playwright install chromium ``` 3. Keep the nav byte-identical One shared file, compared on every generation. Files: `lib/screens.ts` ### Done when - [ ] Content sits inside the insets on every screen - [ ] Every tappable element is at least 44 by 44 points or flagged - [ ] The nav is byte-identical across screens - [ ] Remote images load through the proxy ### Watch out - Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. ## Phase 3 · Edits that leave the rest alone An edit changes what was asked and nothing else, and every version is kept. ### Steps 1. Edit from the current file, never from the description The description is six edits out of date. Files: `app/api/edit/route.ts`, `lib/prompts/edit.ts` 2. Version before overwriting History is the undo, so it must exist before the write. Files: `lib/history.ts` 3. Show the diff before accepting A whole-file rewrite should be visible, not discovered. Files: `components/EditDiff.tsx`, `tests/edit-locality.test.ts` ### Done when - [ ] An edit leaves unrelated lines unchanged - [ ] Every version is kept and revert is byte-identical - [ ] The pasted design system stays verbatim - [ ] Large diffs are shown before they apply ### Watch out - An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. ## Phase 4 · The gallery, and pixels at device resolution All screens are reviewed side by side and exported at the density of the phone. ### Steps 1. Build the gallery with an edit box per screen Review and the next instruction in one place. Files: `components/Gallery.tsx`, `components/PhoneFrame.tsx` 2. Render each file alone at device scale Not the gallery page. Assert the dimensions. Files: `scripts/export.ts`, `tests/export-dimensions.test.ts` 3. Proxy remote images CORS failures are otherwise blank rectangles in the export. Files: `app/api/image/route.ts` ### Done when - [ ] Each export is three times the logical size - [ ] A contact sheet exists - [ ] A failed render is reported by name - [ ] Fonts load before screenshots ### Watch out - Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. ## Not in this build - A click-to-edit canvas. - Share links, accounts and hosting. - A chain of tuned model calls per screen. - Sketch recognition beyond passing the image to the model. ## After v1, if you want it - A regeneration preview that shows what a new design system would change before it does - Component extraction, so a card used on four screens becomes one snippet pasted into each - Dark mode as a second design system applied to the same screens ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Billed per token on every screen and every edit. MODEL_API_KEY=sk-...redacted # Required. Copied from the provider's model list on the day you set up; it changes. MODEL_NAME=the provider's current model id # Required. One file per screen, with .history beside it. Back this folder up. SCREENS_DIR=./public/screens # Required. Logical width and height of the target device. The example is a 2622 by 1206 pixel display at scale three; check yours. DEVICE_SIZE_PT=402x874 # Required. Top and bottom safe-area insets, measured on a simulator for your device. Do not copy the example. SAFE_INSETS_PT=62,34 # Required. The iOS default control size in Apple's guidelines. The check flags anything smaller. MIN_CONTROL_PT=44 # Required. Device scale factor for screenshots. Output dimensions are asserted against it. EXPORT_SCALE=3 # Required. Refuses further calls at this total. A warning is read as permission. PROJECT_COST_CEILING_MINOR=2000
You are building a production product version of Sleek. 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 ===== # Sleek · product brief ## Problem One screen is already a yes, and you do not need this site for it: paste "design me a settings screen for an iOS app, Tailwind, 402x874" into the agent you already pay for and you get something decent. What Sleek sells is everything that makes twelve screens look like the same app, and most of that turns out to be prompt discipline rather than product. Decide the design system once, paste it verbatim into every later call, and hand the model the mobile conventions it does not apply unprompted: safe areas, 44px tap targets, native navigation instead of a website cropped to phone width. Two gaps survive that. There is no canvas, so every change is a re-prompt of a whole file rather than a click, and one call to one model is a rougher first draft than a tuned chain of them, which you pay for in your own iteration time. The Figma handoff is not one of the gaps: Sleek's own team ships the converter as MIT on npm, so the DIY build gets editable layers from one install. ## Product outcome A folder of screens that look like one app, checked against the phone's own rules rather than by eye, with every edit a diff and an export that lands in Figma as layers. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - LLM API key - Node 22+ - Playwright for screenshot export ## Explicit non-goals for v1 - A click-to-edit canvas. - Share links, accounts and hosting. - A chain of tuned model calls per screen. - Sketch recognition beyond passing the image to the model. - clicking an element to change it instead of re-prompting the whole file - a tuned multi-step pipeline · you get one prompt on one model, and you close the gap in your own iteration time - sketch input · photograph a hand-drawn screen and get it back as a design - share links, project history, and team seats ## Success criteria - Generating a screen is confirmed to leave the design system byte-identical - Every tappable element is confirmed measured at 44 by 44 points or flagged - An edit is confirmed to leave unrelated lines unchanged - Exports are confirmed at three times the logical size - The Figma copy is confirmed to fail visibly outside a gesture ===== BRIEF.md ===== # Build brief · Sleek 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 multi-screen mobile app designer that keeps screen nine looking like screen one to replace Sleek. 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. ### Stack (fixed, do not substitute) - Runtime: Next.js with the App Router, screens as files on disk, no database · a folder of HTML files needs no schema and every edit is a diff - Model: One provider behind one streaming route handler, model name in the environment · the product is prompt discipline rather than a model - Screens: One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame · a file per screen makes history, diffing and export trivial - Consistency: design-system.json and _nav.html generated once and pasted verbatim into every later call · this is what stops screen nine drifting from screen one - Export: Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers · the converter Sleek itself ships is on npm under MIT ### Data model (create this before Phase 1) - project.json(name, description, device, width_pt, height_pt, safe_top_pt, safe_bottom_pt, created_at) · the target device, measured rather than guessed - design-system.json(colours[5], fonts[2], type_scale[6], radius, spacing_unit, icon_set) · written once, then read only - _nav.html · the shared navigation, generated with the design system and byte-identical in every screen - screens/<name>.html · one standalone file per screen, the only thing the model rewrites - screens/.history/<name>/<timestamp>.html · every version ever produced, never pruned automatically - calls.jsonl(at, screen, purpose, model, tokens_in, tokens_out, cost_minor) · one line per model call, so the bill is visible before it arrives - The design system is generated once and copied into every prompt verbatim, because letting later calls improve it is how the twelfth screen stops looking like the first. ### Phase 1 · The screen list and the design system, decided once Build: Take a description of the app, or a photographed sketch, and answer first with six to ten screen names with one line of purpose each, which the person edits before anything is built. The same call writes design-system.json and _nav.html. Lock both: regenerating is an explicit action that versions the old files, never a side effect of a later call. Trap: Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. Done when: The screen list is editable before any screen is generated. design-system.json holds five colours, two fonts, a six-step type scale, a radius, a spacing unit and an icon set, and _nav.html exists. Generating a screen leaves both files byte-identical, tested by hashing them before and after. Every model call appends a line to calls.jsonl with tokens and cost. Do not build yet: No screens, edits, gallery or export. ### Phase 2 · Screens that obey the phone, checked by machine Build: Generate each screen as one standalone HTML file at the device's logical size, with the safe-area insets as CSS variables, the design system and nav pasted verbatim, and the mobile conventions spelled out in the prompt. Then open the file headlessly and measure: content inside the insets, every interactive element at least the platform's default control size, the nav byte-identical. A screen that fails is regenerated with the failures named. Trap: Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. Done when: Every screen renders at the target logical size with all content inside the safe-area insets, measured with Playwright. Every interactive element is at least 44 by 44 points, the iOS default control size in Apple's guidelines and the enhanced target size in WCAG, or the screen is flagged with the offending element named. The nav block in every screen is byte-identical to _nav.html. A screen with a remote image loads it through the image proxy. Do not build yet: No edits, gallery or export yet. ### Phase 3 · Edits that leave the rest alone Build: An edit re-sends the current file plus the instruction and overwrites the file in place, after saving the previous version to history. Show a diff of what changed before it is accepted, and make revert one action. Trap: An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. Done when: Editing one component leaves every other line of the file unchanged, asserted by diffing a fixture before and after. Every version is in history with a timestamp and revert restores it byte-identically. The design system and nav pasted into the edit prompt are still verbatim. A diff that touches far more than the instruction implies is shown before it is accepted rather than applied silently. Do not build yet: No gallery, export or Figma yet. ### Phase 4 · The gallery, and pixels at device resolution Build: A gallery of every screen in a phone frame, side by side, with an edit box under each. An export command renders each file in its own page with Playwright at device scale three into exports/, plus a contact sheet. Remote images go through a small proxy so they render. Trap: Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. Done when: Export produces one image per screen whose pixel dimensions are three times the logical size, asserted by reading the files. A contact sheet of all screens is produced. A screen that fails to render is reported by name rather than skipped. Web fonts are loaded before the screenshot is taken. Do not build yet: No Figma export or cost ceiling yet. ### Phase 5 · Editable layers, a ceiling, and what this is not Build: A copy-all-to-Figma button using @figit/dom-to-figma: one frame per screen from the iframe's document body, converted, then written to the clipboard from the click handler. A per-project cost ceiling that refuses further calls. A README that states there is no canvas and every change is a re-prompt. Trap: Writing to the clipboard after an awaited conversion that started from a click can fall outside the user activation window, and Safari and Firefox refuse the write silently. Hand the pending conversion to the clipboard item as a promise inside the click handler, and show a failure message rather than assuming the paste will work. Done when: Pasting into Figma yields editable layers for every screen, checked by hand once, and the clipboard payload is asserted non-empty in a test. A copy attempted outside a user gesture shows a visible failure. A project whose logged cost reaches the ceiling refuses further model calls with the total shown. The README states that there is no canvas and that every change is a re-prompt of a whole file. Do not build yet: Nothing after this. ### Out of scope (and why) - A click-to-edit canvas, which is the product's real work and would take longer than everything else here combined; a diff after a re-prompt is the honest substitute. - Share links, accounts and hosting, since the tool runs on your machine and the screens are files. - A chain of tuned model calls per screen, which is what months of the vendor's iteration bought and which one prompt on one model approximates roughly. - Sketch recognition beyond handing the photograph to the model, which is a research problem rather than a weekend. ### README must contain - That the design system and nav are generated once and pasted verbatim, and that regenerating them is destructive and versioned - That insets and tap targets are measured in a headless browser, because the eye is what the model fooled - That every edit works from the current file and is a diff with history, never a regeneration from the description - That the Figma copy must be fired from a real click and fails visibly otherwise - That every screen is a model call and the project ceiling refuses rather than warns ===== ARCHITECTURE.md ===== # Architecture · Sleek ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Next.js with the App Router, screens as files on disk, no database | a folder of HTML files needs no schema and every edit is a diff | | Model | One provider behind one streaming route handler, model name in the environment | the product is prompt discipline rather than a model | | Screens | One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame | a file per screen makes history, diffing and export trivial | | Consistency | design-system.json and _nav.html generated once and pasted verbatim into every later call | this is what stops screen nine drifting from screen one | | Export | Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers | the converter Sleek itself ships is on npm under MIT | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Planning | The screen list and the locked design system. | Any model, provided the design system is written once and read thereafter. | | Generation | Standalone screens with conventions in the prompt and checks after. | Any prompt, provided insets and control sizes are measured rather than assumed. | | Editing | Edits from the current file with history and a diff. | Not swappable for regeneration from the description, which loses every earlier edit. | | Export | Device-scale renders and Figma layers. | Any renderer, provided dimensions are asserted and the clipboard write happens inside the click. | | Budget | The cost log and the ceiling. | Any accounting, provided the ceiling refuses rather than warns. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `MODEL_API_KEY` · required, secret · Billed per token on every screen and every edit. - `MODEL_NAME` · required · Copied from the provider's model list on the day you set up; it changes. - `SCREENS_DIR` · required · One file per screen, with .history beside it. Back this folder up. - `DEVICE_SIZE_PT` · required · Logical width and height of the target device. The example is a 2622 by 1206 pixel display at scale three; check yours. - `SAFE_INSETS_PT` · required · Top and bottom safe-area insets, measured on a simulator for your device. Do not copy the example. - `MIN_CONTROL_PT` · required · The iOS default control size in Apple's guidelines. The check flags anything smaller. - `EXPORT_SCALE` · required · Device scale factor for screenshots. Output dimensions are asserted against it. - `PROJECT_COST_CEILING_MINOR` · required · Refuses further calls at this total. A warning is read as permission. ## 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 · Sleek product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Next.js with the App Router, screens as files on disk, no database, One provider behind one streaming route handler, model name in the environment, One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame, design-system.json and _nav.html generated once and pasted verbatim into every later call, Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers. - 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 - Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. - Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. - An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. - Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. - Writing to the clipboard after an awaited conversion that started from a click can fall outside the user activation window, and Safari and Firefox refuse the write silently. Hand the pending conversion to the clipboard item as a promise inside the click handler, and show a failure message rather than assuming the paste will work. ===== MILESTONES.md ===== # Delivery milestones · Sleek Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · The screen list and the design system, decided once One call plans the app and fixes its design system, and nothing regenerates that later. ### Steps 1. Plan the screen list before building anything Editing a name is free and regenerating a screen is not. Files: `app/api/plan/route.ts`, `lib/prompts/plan.ts` 2. Write the design system once and lock it Every later prompt reads it; nothing later writes it. Files: `lib/design-system.ts`, `tests/design-system-locked.test.ts` 3. Log every call with its cost The bill should be visible before it arrives. Files: `lib/calls.ts` ### Done when - [ ] The screen list is editable before generation - [ ] The design system has all six parts and the nav exists - [ ] Generating a screen leaves both files byte-identical - [ ] Every call is logged with tokens and cost ### Watch out - Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. ## M2 · Screens that obey the phone, checked by machine Each screen is a standalone file that respects insets, tap targets and native navigation. ### Steps 1. Give the model the conventions it will not apply alone Safe areas, control sizes, native navigation, no hover states. Files: `lib/prompts/screen.ts`, `app/api/screen/route.ts` 2. Measure insets and tap targets mechanically Bounding boxes in a headless browser, not a glance at the gallery. Files: `scripts/check-screens.ts`, `tests/tap-targets.test.ts` ```sh npx playwright install chromium ``` 3. Keep the nav byte-identical One shared file, compared on every generation. Files: `lib/screens.ts` ### Done when - [ ] Content sits inside the insets on every screen - [ ] Every tappable element is at least 44 by 44 points or flagged - [ ] The nav is byte-identical across screens - [ ] Remote images load through the proxy ### Watch out - Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. ## M3 · Edits that leave the rest alone An edit changes what was asked and nothing else, and every version is kept. ### Steps 1. Edit from the current file, never from the description The description is six edits out of date. Files: `app/api/edit/route.ts`, `lib/prompts/edit.ts` 2. Version before overwriting History is the undo, so it must exist before the write. Files: `lib/history.ts` 3. Show the diff before accepting A whole-file rewrite should be visible, not discovered. Files: `components/EditDiff.tsx`, `tests/edit-locality.test.ts` ### Done when - [ ] An edit leaves unrelated lines unchanged - [ ] Every version is kept and revert is byte-identical - [ ] The pasted design system stays verbatim - [ ] Large diffs are shown before they apply ### Watch out - An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. ## M4 · The gallery, and pixels at device resolution All screens are reviewed side by side and exported at the density of the phone. ### Steps 1. Build the gallery with an edit box per screen Review and the next instruction in one place. Files: `components/Gallery.tsx`, `components/PhoneFrame.tsx` 2. Render each file alone at device scale Not the gallery page. Assert the dimensions. Files: `scripts/export.ts`, `tests/export-dimensions.test.ts` 3. Proxy remote images CORS failures are otherwise blank rectangles in the export. Files: `app/api/image/route.ts` ### Done when - [ ] Each export is three times the logical size - [ ] A contact sheet exists - [ ] A failed render is reported by name - [ ] Fonts load before screenshots ### Watch out - Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. ## M5 · Editable layers, a ceiling, and what this is not (production only) Screens land in Figma as editable layers, spending stops at a limit, and the README says what a canvas would have given you. ### Steps 1. Convert and write inside the click Pass the pending conversion to the clipboard item; do not await first. Files: `components/CopyToFigma.tsx` 2. Refuse past the ceiling With the total shown, not a warning. Files: `lib/budget.ts`, `tests/ceiling.test.ts` 3. Say what a canvas would have given you Honesty in the README beats a half-built editor. Files: `README.md` ### Done when - [ ] Pasting into Figma gives editable layers - [ ] A copy outside a gesture fails visibly - [ ] The ceiling refuses with the total shown - [ ] The README states there is no canvas ### Watch out - Writing to the clipboard after an awaited conversion that started from a click can fall outside the user activation window, and Safari and Firefox refuse the write silently. Hand the pending conversion to the clipboard item as a promise inside the click handler, and show a failure message rather than assuming the paste will work. ===== OPERATIONS.md ===== # Operations · Sleek ## Backup The screens folder including .history is the project; the design system is the one file you cannot regenerate identically. Keep the folder in ordinary backups and commit it to version control, since it is text. ## Restore Restore the folder and confirm the design-system.json hash matches the one recorded in calls.jsonl for the screens, since screens built against a different system will drift on the next edit. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Cost per screen and the check failure rate. A rising failure rate with an unchanged prompt means the model behind the name changed, which is the moment to re-run the checks over every screen. ## Incident checklist A regenerated design system is the incident: every later screen inherits the new one and the older screens do not. History holds the previous version; restore it, then re-check every screen against 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 - [ ] Generating a screen is confirmed to leave the design system byte-identical - [ ] Every tappable element is confirmed measured at 44 by 44 points or flagged - [ ] An edit is confirmed to leave unrelated lines unchanged - [ ] Exports are confirmed at three times the logical size - [ ] The Figma copy is confirmed to fail visibly outside a gesture ## Launch constraint Do not market omitted Sleek 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 · secret. Billed per token on every screen and every edit. MODEL_API_KEY=sk-...redacted # Required. Copied from the provider's model list on the day you set up; it changes. MODEL_NAME=the provider's current model id # Required. One file per screen, with .history beside it. Back this folder up. SCREENS_DIR=./public/screens # Required. Logical width and height of the target device. The example is a 2622 by 1206 pixel display at scale three; check yours. DEVICE_SIZE_PT=402x874 # Required. Top and bottom safe-area insets, measured on a simulator for your device. Do not copy the example. SAFE_INSETS_PT=62,34 # Required. The iOS default control size in Apple's guidelines. The check flags anything smaller. MIN_CONTROL_PT=44 # Required. Device scale factor for screenshots. Output dimensions are asserted against it. EXPORT_SCALE=3 # Required. Refuses further calls at this total. A warning is read as permission. PROJECT_COST_CEILING_MINOR=2000
# Sleek · indie build A prompt-to-screen tool for mobile apps where one screen is easy and the product is everything that makes twelve screens look like one app: a design system decided once and never regenerated, mobile conventions the model does not apply unprompted and so must be measured rather than eyeballed, edits that leave the rest of a file alone, and a Figma export that only works from a real click. Estimated effort: **weekend**. 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 | Next.js with the App Router, screens as files on disk, no database | a folder of HTML files needs no schema and every edit is a diff | | Model | One provider behind one streaming route handler, model name in the environment | the product is prompt discipline rather than a model | | Screens | One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame | a file per screen makes history, diffing and export trivial | | Consistency | design-system.json and _nav.html generated once and pasted verbatim into every later call | this is what stops screen nine drifting from screen one | | Export | Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers | the converter Sleek itself ships is on npm under MIT | ## 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 application, the checks and the export. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **An API key for a model that accepts images** · per token, roughly one long call per screen and per edit - Why: The sketch input and every screen are calls to it, and the cost per screen is what the ceiling protects you from. - Get it: A provider account with per-token billing; note the price of a long output before generating ten screens. - Verify: One screen generates and its cost appears in calls.jsonl - [ ] **Playwright with Chromium** · free - Why: It measures insets and tap targets and renders the exports; without it every check is by eye. - Get it: npm install playwright, then npx playwright install chromium. - Verify: A headless page opens and reports an element's bounding box - [ ] **The target device, with its logical size and insets measured** · free - Why: Every screen is generated at one size and checked against one set of insets, and a guessed inset means content under the status bar on the real phone. - Get it: Pick the device, take its logical size from the pixel resolution divided by the scale factor, and measure the safe-area insets on a simulator. - Verify: project.json holds the width, height and both insets with a note on where they came from - [ ] **A Figma account to test the paste** (optional) · free - Why: Editable layers are the export that matters and the only way to confirm them is to paste. - Get it: A free Figma account and an empty file. - Verify: A pasted screen appears as layers rather than as an image ## Quick start ```sh npx playwright install chromium ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - A click-to-edit canvas. - Share links, accounts and hosting. - A chain of tuned model calls per screen. - Sketch recognition beyond passing the image to the model. - clicking an element to change it instead of re-prompting the whole file - a tuned multi-step pipeline · you get one prompt on one model, and you close the gap in your own iteration time - sketch input · photograph a hand-drawn screen and get it back as a design - share links, project history, and team seats If one of those is essential to you, that is the reason to keep paying for Sleek, and the README should say so rather than pretend.
# Build brief · Sleek 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 multi-screen mobile app designer that keeps screen nine looking like screen one to replace Sleek. 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. ### Stack (fixed, do not substitute) - Runtime: Next.js with the App Router, screens as files on disk, no database · a folder of HTML files needs no schema and every edit is a diff - Model: One provider behind one streaming route handler, model name in the environment · the product is prompt discipline rather than a model - Screens: One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame · a file per screen makes history, diffing and export trivial - Consistency: design-system.json and _nav.html generated once and pasted verbatim into every later call · this is what stops screen nine drifting from screen one - Export: Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers · the converter Sleek itself ships is on npm under MIT ### Data model (create this before Phase 1) - project.json(name, description, device, width_pt, height_pt, safe_top_pt, safe_bottom_pt, created_at) · the target device, measured rather than guessed - design-system.json(colours[5], fonts[2], type_scale[6], radius, spacing_unit, icon_set) · written once, then read only - _nav.html · the shared navigation, generated with the design system and byte-identical in every screen - screens/<name>.html · one standalone file per screen, the only thing the model rewrites - screens/.history/<name>/<timestamp>.html · every version ever produced, never pruned automatically - calls.jsonl(at, screen, purpose, model, tokens_in, tokens_out, cost_minor) · one line per model call, so the bill is visible before it arrives - The design system is generated once and copied into every prompt verbatim, because letting later calls improve it is how the twelfth screen stops looking like the first. ### Phase 1 · The screen list and the design system, decided once Build: Take a description of the app, or a photographed sketch, and answer first with six to ten screen names with one line of purpose each, which the person edits before anything is built. The same call writes design-system.json and _nav.html. Lock both: regenerating is an explicit action that versions the old files, never a side effect of a later call. Trap: Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. Done when: The screen list is editable before any screen is generated. design-system.json holds five colours, two fonts, a six-step type scale, a radius, a spacing unit and an icon set, and _nav.html exists. Generating a screen leaves both files byte-identical, tested by hashing them before and after. Every model call appends a line to calls.jsonl with tokens and cost. Do not build yet: No screens, edits, gallery or export. ### Phase 2 · Screens that obey the phone, checked by machine Build: Generate each screen as one standalone HTML file at the device's logical size, with the safe-area insets as CSS variables, the design system and nav pasted verbatim, and the mobile conventions spelled out in the prompt. Then open the file headlessly and measure: content inside the insets, every interactive element at least the platform's default control size, the nav byte-identical. A screen that fails is regenerated with the failures named. Trap: Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. Done when: Every screen renders at the target logical size with all content inside the safe-area insets, measured with Playwright. Every interactive element is at least 44 by 44 points, the iOS default control size in Apple's guidelines and the enhanced target size in WCAG, or the screen is flagged with the offending element named. The nav block in every screen is byte-identical to _nav.html. A screen with a remote image loads it through the image proxy. Do not build yet: No edits, gallery or export yet. ### Phase 3 · Edits that leave the rest alone Build: An edit re-sends the current file plus the instruction and overwrites the file in place, after saving the previous version to history. Show a diff of what changed before it is accepted, and make revert one action. Trap: An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. Done when: Editing one component leaves every other line of the file unchanged, asserted by diffing a fixture before and after. Every version is in history with a timestamp and revert restores it byte-identically. The design system and nav pasted into the edit prompt are still verbatim. A diff that touches far more than the instruction implies is shown before it is accepted rather than applied silently. Do not build yet: No gallery, export or Figma yet. ### Phase 4 · The gallery, and pixels at device resolution Build: A gallery of every screen in a phone frame, side by side, with an edit box under each. An export command renders each file in its own page with Playwright at device scale three into exports/, plus a contact sheet. Remote images go through a small proxy so they render. Trap: Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. Done when: Export produces one image per screen whose pixel dimensions are three times the logical size, asserted by reading the files. A contact sheet of all screens is produced. A screen that fails to render is reported by name rather than skipped. Web fonts are loaded before the screenshot is taken. Do not build yet: No Figma export or cost ceiling yet. ### Phase 5 · Editable layers, a ceiling, and what this is not Build: A copy-all-to-Figma button using @figit/dom-to-figma: one frame per screen from the iframe's document body, converted, then written to the clipboard from the click handler. A per-project cost ceiling that refuses further calls. A README that states there is no canvas and every change is a re-prompt. Trap: Writing to the clipboard after an awaited conversion that started from a click can fall outside the user activation window, and Safari and Firefox refuse the write silently. Hand the pending conversion to the clipboard item as a promise inside the click handler, and show a failure message rather than assuming the paste will work. Done when: Pasting into Figma yields editable layers for every screen, checked by hand once, and the clipboard payload is asserted non-empty in a test. A copy attempted outside a user gesture shows a visible failure. A project whose logged cost reaches the ceiling refuses further model calls with the total shown. The README states that there is no canvas and that every change is a re-prompt of a whole file. Do not build yet: Nothing after this. ### Out of scope (and why) - A click-to-edit canvas, which is the product's real work and would take longer than everything else here combined; a diff after a re-prompt is the honest substitute. - Share links, accounts and hosting, since the tool runs on your machine and the screens are files. - A chain of tuned model calls per screen, which is what months of the vendor's iteration bought and which one prompt on one model approximates roughly. - Sketch recognition beyond handing the photograph to the model, which is a research problem rather than a weekend. ### README must contain - That the design system and nav are generated once and pasted verbatim, and that regenerating them is destructive and versioned - That insets and tap targets are measured in a headless browser, because the eye is what the model fooled - That every edit works from the current file and is a diff with history, never a regeneration from the description - That the Figma copy must be fired from a real click and fails visibly otherwise - That every screen is a model call and the project ceiling refuses rather than warns
# Agent instructions · Sleek indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Next.js with the App Router, screens as files on disk, no database, One provider behind one streaming route handler, model name in the environment, One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame, design-system.json and _nav.html generated once and pasted verbatim into every later call, Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers. 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 - Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. - Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. - An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. - Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions.
# Build plan · Sleek A prompt-to-screen tool for mobile apps where one screen is easy and the product is everything that makes twelve screens look like one app: a design system decided once and never regenerated, mobile conventions the model does not apply unprompted and so must be measured rather than eyeballed, edits that leave the rest of a file alone, and a Figma export that only works from a real click. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · The screen list and the design system, decided once One call plans the app and fixes its design system, and nothing regenerates that later. ### Steps 1. Plan the screen list before building anything Editing a name is free and regenerating a screen is not. Files: `app/api/plan/route.ts`, `lib/prompts/plan.ts` 2. Write the design system once and lock it Every later prompt reads it; nothing later writes it. Files: `lib/design-system.ts`, `tests/design-system-locked.test.ts` 3. Log every call with its cost The bill should be visible before it arrives. Files: `lib/calls.ts` ### Done when - [ ] The screen list is editable before generation - [ ] The design system has all six parts and the nav exists - [ ] Generating a screen leaves both files byte-identical - [ ] Every call is logged with tokens and cost ### Watch out - Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. ## Phase 2 · Screens that obey the phone, checked by machine Each screen is a standalone file that respects insets, tap targets and native navigation. ### Steps 1. Give the model the conventions it will not apply alone Safe areas, control sizes, native navigation, no hover states. Files: `lib/prompts/screen.ts`, `app/api/screen/route.ts` 2. Measure insets and tap targets mechanically Bounding boxes in a headless browser, not a glance at the gallery. Files: `scripts/check-screens.ts`, `tests/tap-targets.test.ts` ```sh npx playwright install chromium ``` 3. Keep the nav byte-identical One shared file, compared on every generation. Files: `lib/screens.ts` ### Done when - [ ] Content sits inside the insets on every screen - [ ] Every tappable element is at least 44 by 44 points or flagged - [ ] The nav is byte-identical across screens - [ ] Remote images load through the proxy ### Watch out - Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. ## Phase 3 · Edits that leave the rest alone An edit changes what was asked and nothing else, and every version is kept. ### Steps 1. Edit from the current file, never from the description The description is six edits out of date. Files: `app/api/edit/route.ts`, `lib/prompts/edit.ts` 2. Version before overwriting History is the undo, so it must exist before the write. Files: `lib/history.ts` 3. Show the diff before accepting A whole-file rewrite should be visible, not discovered. Files: `components/EditDiff.tsx`, `tests/edit-locality.test.ts` ### Done when - [ ] An edit leaves unrelated lines unchanged - [ ] Every version is kept and revert is byte-identical - [ ] The pasted design system stays verbatim - [ ] Large diffs are shown before they apply ### Watch out - An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. ## Phase 4 · The gallery, and pixels at device resolution All screens are reviewed side by side and exported at the density of the phone. ### Steps 1. Build the gallery with an edit box per screen Review and the next instruction in one place. Files: `components/Gallery.tsx`, `components/PhoneFrame.tsx` 2. Render each file alone at device scale Not the gallery page. Assert the dimensions. Files: `scripts/export.ts`, `tests/export-dimensions.test.ts` 3. Proxy remote images CORS failures are otherwise blank rectangles in the export. Files: `app/api/image/route.ts` ### Done when - [ ] Each export is three times the logical size - [ ] A contact sheet exists - [ ] A failed render is reported by name - [ ] Fonts load before screenshots ### Watch out - Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. ## Not in this build - A click-to-edit canvas. - Share links, accounts and hosting. - A chain of tuned model calls per screen. - Sketch recognition beyond passing the image to the model. ## After v1, if you want it - A regeneration preview that shows what a new design system would change before it does - Component extraction, so a card used on four screens becomes one snippet pasted into each - Dark mode as a second design system applied to the same screens
# Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Billed per token on every screen and every edit. MODEL_API_KEY=sk-...redacted # Required. Copied from the provider's model list on the day you set up; it changes. MODEL_NAME=the provider's current model id # Required. One file per screen, with .history beside it. Back this folder up. SCREENS_DIR=./public/screens # Required. Logical width and height of the target device. The example is a 2622 by 1206 pixel display at scale three; check yours. DEVICE_SIZE_PT=402x874 # Required. Top and bottom safe-area insets, measured on a simulator for your device. Do not copy the example. SAFE_INSETS_PT=62,34 # Required. The iOS default control size in Apple's guidelines. The check flags anything smaller. MIN_CONTROL_PT=44 # Required. Device scale factor for screenshots. Output dimensions are asserted against it. EXPORT_SCALE=3 # Required. Refuses further calls at this total. A warning is read as permission. PROJECT_COST_CEILING_MINOR=2000
# Sleek · product brief ## Problem One screen is already a yes, and you do not need this site for it: paste "design me a settings screen for an iOS app, Tailwind, 402x874" into the agent you already pay for and you get something decent. What Sleek sells is everything that makes twelve screens look like the same app, and most of that turns out to be prompt discipline rather than product. Decide the design system once, paste it verbatim into every later call, and hand the model the mobile conventions it does not apply unprompted: safe areas, 44px tap targets, native navigation instead of a website cropped to phone width. Two gaps survive that. There is no canvas, so every change is a re-prompt of a whole file rather than a click, and one call to one model is a rougher first draft than a tuned chain of them, which you pay for in your own iteration time. The Figma handoff is not one of the gaps: Sleek's own team ships the converter as MIT on npm, so the DIY build gets editable layers from one install. ## Product outcome A folder of screens that look like one app, checked against the phone's own rules rather than by eye, with every edit a diff and an export that lands in Figma as layers. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - LLM API key - Node 22+ - Playwright for screenshot export ## Explicit non-goals for v1 - A click-to-edit canvas. - Share links, accounts and hosting. - A chain of tuned model calls per screen. - Sketch recognition beyond passing the image to the model. - clicking an element to change it instead of re-prompting the whole file - a tuned multi-step pipeline · you get one prompt on one model, and you close the gap in your own iteration time - sketch input · photograph a hand-drawn screen and get it back as a design - share links, project history, and team seats ## Success criteria - Generating a screen is confirmed to leave the design system byte-identical - Every tappable element is confirmed measured at 44 by 44 points or flagged - An edit is confirmed to leave unrelated lines unchanged - Exports are confirmed at three times the logical size - The Figma copy is confirmed to fail visibly outside a gesture
# Build brief · Sleek 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 multi-screen mobile app designer that keeps screen nine looking like screen one to replace Sleek. 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. ### Stack (fixed, do not substitute) - Runtime: Next.js with the App Router, screens as files on disk, no database · a folder of HTML files needs no schema and every edit is a diff - Model: One provider behind one streaming route handler, model name in the environment · the product is prompt discipline rather than a model - Screens: One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame · a file per screen makes history, diffing and export trivial - Consistency: design-system.json and _nav.html generated once and pasted verbatim into every later call · this is what stops screen nine drifting from screen one - Export: Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers · the converter Sleek itself ships is on npm under MIT ### Data model (create this before Phase 1) - project.json(name, description, device, width_pt, height_pt, safe_top_pt, safe_bottom_pt, created_at) · the target device, measured rather than guessed - design-system.json(colours[5], fonts[2], type_scale[6], radius, spacing_unit, icon_set) · written once, then read only - _nav.html · the shared navigation, generated with the design system and byte-identical in every screen - screens/<name>.html · one standalone file per screen, the only thing the model rewrites - screens/.history/<name>/<timestamp>.html · every version ever produced, never pruned automatically - calls.jsonl(at, screen, purpose, model, tokens_in, tokens_out, cost_minor) · one line per model call, so the bill is visible before it arrives - The design system is generated once and copied into every prompt verbatim, because letting later calls improve it is how the twelfth screen stops looking like the first. ### Phase 1 · The screen list and the design system, decided once Build: Take a description of the app, or a photographed sketch, and answer first with six to ten screen names with one line of purpose each, which the person edits before anything is built. The same call writes design-system.json and _nav.html. Lock both: regenerating is an explicit action that versions the old files, never a side effect of a later call. Trap: Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. Done when: The screen list is editable before any screen is generated. design-system.json holds five colours, two fonts, a six-step type scale, a radius, a spacing unit and an icon set, and _nav.html exists. Generating a screen leaves both files byte-identical, tested by hashing them before and after. Every model call appends a line to calls.jsonl with tokens and cost. Do not build yet: No screens, edits, gallery or export. ### Phase 2 · Screens that obey the phone, checked by machine Build: Generate each screen as one standalone HTML file at the device's logical size, with the safe-area insets as CSS variables, the design system and nav pasted verbatim, and the mobile conventions spelled out in the prompt. Then open the file headlessly and measure: content inside the insets, every interactive element at least the platform's default control size, the nav byte-identical. A screen that fails is regenerated with the failures named. Trap: Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. Done when: Every screen renders at the target logical size with all content inside the safe-area insets, measured with Playwright. Every interactive element is at least 44 by 44 points, the iOS default control size in Apple's guidelines and the enhanced target size in WCAG, or the screen is flagged with the offending element named. The nav block in every screen is byte-identical to _nav.html. A screen with a remote image loads it through the image proxy. Do not build yet: No edits, gallery or export yet. ### Phase 3 · Edits that leave the rest alone Build: An edit re-sends the current file plus the instruction and overwrites the file in place, after saving the previous version to history. Show a diff of what changed before it is accepted, and make revert one action. Trap: An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. Done when: Editing one component leaves every other line of the file unchanged, asserted by diffing a fixture before and after. Every version is in history with a timestamp and revert restores it byte-identically. The design system and nav pasted into the edit prompt are still verbatim. A diff that touches far more than the instruction implies is shown before it is accepted rather than applied silently. Do not build yet: No gallery, export or Figma yet. ### Phase 4 · The gallery, and pixels at device resolution Build: A gallery of every screen in a phone frame, side by side, with an edit box under each. An export command renders each file in its own page with Playwright at device scale three into exports/, plus a contact sheet. Remote images go through a small proxy so they render. Trap: Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. Done when: Export produces one image per screen whose pixel dimensions are three times the logical size, asserted by reading the files. A contact sheet of all screens is produced. A screen that fails to render is reported by name rather than skipped. Web fonts are loaded before the screenshot is taken. Do not build yet: No Figma export or cost ceiling yet. ### Phase 5 · Editable layers, a ceiling, and what this is not Build: A copy-all-to-Figma button using @figit/dom-to-figma: one frame per screen from the iframe's document body, converted, then written to the clipboard from the click handler. A per-project cost ceiling that refuses further calls. A README that states there is no canvas and every change is a re-prompt. Trap: Writing to the clipboard after an awaited conversion that started from a click can fall outside the user activation window, and Safari and Firefox refuse the write silently. Hand the pending conversion to the clipboard item as a promise inside the click handler, and show a failure message rather than assuming the paste will work. Done when: Pasting into Figma yields editable layers for every screen, checked by hand once, and the clipboard payload is asserted non-empty in a test. A copy attempted outside a user gesture shows a visible failure. A project whose logged cost reaches the ceiling refuses further model calls with the total shown. The README states that there is no canvas and that every change is a re-prompt of a whole file. Do not build yet: Nothing after this. ### Out of scope (and why) - A click-to-edit canvas, which is the product's real work and would take longer than everything else here combined; a diff after a re-prompt is the honest substitute. - Share links, accounts and hosting, since the tool runs on your machine and the screens are files. - A chain of tuned model calls per screen, which is what months of the vendor's iteration bought and which one prompt on one model approximates roughly. - Sketch recognition beyond handing the photograph to the model, which is a research problem rather than a weekend. ### README must contain - That the design system and nav are generated once and pasted verbatim, and that regenerating them is destructive and versioned - That insets and tap targets are measured in a headless browser, because the eye is what the model fooled - That every edit works from the current file and is a diff with history, never a regeneration from the description - That the Figma copy must be fired from a real click and fails visibly otherwise - That every screen is a model call and the project ceiling refuses rather than warns
# Architecture · Sleek ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Next.js with the App Router, screens as files on disk, no database | a folder of HTML files needs no schema and every edit is a diff | | Model | One provider behind one streaming route handler, model name in the environment | the product is prompt discipline rather than a model | | Screens | One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame | a file per screen makes history, diffing and export trivial | | Consistency | design-system.json and _nav.html generated once and pasted verbatim into every later call | this is what stops screen nine drifting from screen one | | Export | Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers | the converter Sleek itself ships is on npm under MIT | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Planning | The screen list and the locked design system. | Any model, provided the design system is written once and read thereafter. | | Generation | Standalone screens with conventions in the prompt and checks after. | Any prompt, provided insets and control sizes are measured rather than assumed. | | Editing | Edits from the current file with history and a diff. | Not swappable for regeneration from the description, which loses every earlier edit. | | Export | Device-scale renders and Figma layers. | Any renderer, provided dimensions are asserted and the clipboard write happens inside the click. | | Budget | The cost log and the ceiling. | Any accounting, provided the ceiling refuses rather than warns. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `MODEL_API_KEY` · required, secret · Billed per token on every screen and every edit. - `MODEL_NAME` · required · Copied from the provider's model list on the day you set up; it changes. - `SCREENS_DIR` · required · One file per screen, with .history beside it. Back this folder up. - `DEVICE_SIZE_PT` · required · Logical width and height of the target device. The example is a 2622 by 1206 pixel display at scale three; check yours. - `SAFE_INSETS_PT` · required · Top and bottom safe-area insets, measured on a simulator for your device. Do not copy the example. - `MIN_CONTROL_PT` · required · The iOS default control size in Apple's guidelines. The check flags anything smaller. - `EXPORT_SCALE` · required · Device scale factor for screenshots. Output dimensions are asserted against it. - `PROJECT_COST_CEILING_MINOR` · required · Refuses further calls at this total. A warning is read as permission. ## 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 · Sleek product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Next.js with the App Router, screens as files on disk, no database, One provider behind one streaming route handler, model name in the environment, One standalone HTML file per screen with Tailwind from a CDN and an icon set, shown in a phone frame, design-system.json and _nav.html generated once and pasted verbatim into every later call, Playwright screenshots at device scale, and @figit/dom-to-figma for editable layers. - 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 - Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. - Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. - An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. - Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. - Writing to the clipboard after an awaited conversion that started from a click can fall outside the user activation window, and Safari and Firefox refuse the write silently. Hand the pending conversion to the clipboard item as a promise inside the click handler, and show a failure message rather than assuming the paste will work.
# Delivery milestones · Sleek Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · The screen list and the design system, decided once One call plans the app and fixes its design system, and nothing regenerates that later. ### Steps 1. Plan the screen list before building anything Editing a name is free and regenerating a screen is not. Files: `app/api/plan/route.ts`, `lib/prompts/plan.ts` 2. Write the design system once and lock it Every later prompt reads it; nothing later writes it. Files: `lib/design-system.ts`, `tests/design-system-locked.test.ts` 3. Log every call with its cost The bill should be visible before it arrives. Files: `lib/calls.ts` ### Done when - [ ] The screen list is editable before generation - [ ] The design system has all six parts and the nav exists - [ ] Generating a screen leaves both files byte-identical - [ ] Every call is logged with tokens and cost ### Watch out - Letting each screen's call improve the palette or the type scale means every screen quietly gets its own design system, and by screen nine the app looks like four apps built by four people. Generate the system once, paste it verbatim into every later prompt, and make regeneration a deliberate destructive action that keeps the previous version. ## M2 · Screens that obey the phone, checked by machine Each screen is a standalone file that respects insets, tap targets and native navigation. ### Steps 1. Give the model the conventions it will not apply alone Safe areas, control sizes, native navigation, no hover states. Files: `lib/prompts/screen.ts`, `app/api/screen/route.ts` 2. Measure insets and tap targets mechanically Bounding boxes in a headless browser, not a glance at the gallery. Files: `scripts/check-screens.ts`, `tests/tap-targets.test.ts` ```sh npx playwright install chromium ``` 3. Keep the nav byte-identical One shared file, compared on every generation. Files: `lib/screens.ts` ### Done when - [ ] Content sits inside the insets on every screen - [ ] Every tappable element is at least 44 by 44 points or flagged - [ ] The nav is byte-identical across screens - [ ] Remote images load through the proxy ### Watch out - Left to itself the model produces a website cropped to phone width: a top navigation bar, hover states, twelve-pixel links and content under the status bar, and it looks fine in the gallery. Put the conventions in the prompt, then measure bounding boxes in a headless browser rather than judging by eye, because the eye is what the model fooled. ## M3 · Edits that leave the rest alone An edit changes what was asked and nothing else, and every version is kept. ### Steps 1. Edit from the current file, never from the description The description is six edits out of date. Files: `app/api/edit/route.ts`, `lib/prompts/edit.ts` 2. Version before overwriting History is the undo, so it must exist before the write. Files: `lib/history.ts` 3. Show the diff before accepting A whole-file rewrite should be visible, not discovered. Files: `components/EditDiff.tsx`, `tests/edit-locality.test.ts` ### Done when - [ ] An edit leaves unrelated lines unchanged - [ ] Every version is kept and revert is byte-identical - [ ] The pasted design system stays verbatim - [ ] Large diffs are shown before they apply ### Watch out - An edit that regenerates the screen from the original description throws away the six edits before it, and nobody notices until the button they moved yesterday is back where it was. Send the current file rather than the description, show the diff, and keep every version. ## M4 · The gallery, and pixels at device resolution All screens are reviewed side by side and exported at the density of the phone. ### Steps 1. Build the gallery with an edit box per screen Review and the next instruction in one place. Files: `components/Gallery.tsx`, `components/PhoneFrame.tsx` 2. Render each file alone at device scale Not the gallery page. Assert the dimensions. Files: `scripts/export.ts`, `tests/export-dimensions.test.ts` 3. Proxy remote images CORS failures are otherwise blank rectangles in the export. Files: `app/api/image/route.ts` ### Done when - [ ] Each export is three times the logical size - [ ] A contact sheet exists - [ ] A failed render is reported by name - [ ] Fonts load before screenshots ### Watch out - Screenshotting the gallery page captures the iframes at the wrong scale with the wrong fonts, and a review on the phone shows soft text nobody saw on the desktop. Render each screen file in its own page at device scale, and assert the output dimensions. ## M5 · Editable layers, a ceiling, and what this is not (production only) Screens land in Figma as editable layers, spending stops at a limit, and the README says what a canvas would have given you. ### Steps 1. Convert and write inside the click Pass the pending conversion to the clipboard item; do not await first. Files: `components/CopyToFigma.tsx` 2. Refuse past the ceiling With the total shown, not a warning. Files: `lib/budget.ts`, `tests/ceiling.test.ts` 3. Say what a canvas would have given you Honesty in the README beats a half-built editor. Files: `README.md` ### Done when - [ ] Pasting into Figma gives editable layers - [ ] A copy outside a gesture fails visibly - [ ] The ceiling refuses with the total shown - [ ] The README states there is no canvas ### Watch out - Writing to the clipboard after an awaited conversion that started from a click can fall outside the user activation window, and Safari and Firefox refuse the write silently. Hand the pending conversion to the clipboard item as a promise inside the click handler, and show a failure message rather than assuming the paste will work.
# Operations · Sleek ## Backup The screens folder including .history is the project; the design system is the one file you cannot regenerate identically. Keep the folder in ordinary backups and commit it to version control, since it is text. ## Restore Restore the folder and confirm the design-system.json hash matches the one recorded in calls.jsonl for the screens, since screens built against a different system will drift on the next edit. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Cost per screen and the check failure rate. A rising failure rate with an unchanged prompt means the model behind the name changed, which is the moment to re-run the checks over every screen. ## Incident checklist A regenerated design system is the incident: every later screen inherits the new one and the older screens do not. History holds the previous version; restore it, then re-check every screen against 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 - [ ] Generating a screen is confirmed to leave the design system byte-identical - [ ] Every tappable element is confirmed measured at 44 by 44 points or flagged - [ ] An edit is confirmed to leave unrelated lines unchanged - [ ] Exports are confirmed at three times the logical size - [ ] The Figma copy is confirmed to fail visibly outside a gesture ## Launch constraint Do not market omitted Sleek 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 · secret. Billed per token on every screen and every edit. MODEL_API_KEY=sk-...redacted # Required. Copied from the provider's model list on the day you set up; it changes. MODEL_NAME=the provider's current model id # Required. One file per screen, with .history beside it. Back this folder up. SCREENS_DIR=./public/screens # Required. Logical width and height of the target device. The example is a 2622 by 1206 pixel display at scale three; check yours. DEVICE_SIZE_PT=402x874 # Required. Top and bottom safe-area insets, measured on a simulator for your device. Do not copy the example. SAFE_INSETS_PT=62,34 # Required. The iOS default control size in Apple's guidelines. The check flags anything smaller. MIN_CONTROL_PT=44 # Required. Device scale factor for screenshots. Output dimensions are asserted against it. EXPORT_SCALE=3 # Required. Refuses further calls at this total. A warning is read as permission. PROJECT_COST_CEILING_MINOR=2000
$ choose a build depth, inspect the files, then open the complete pack in your agent
Nobody pays for one screen, and that is the honest read on this category: the single-screen version got free the day coding agents got good. What is left is thinner than it looks. There is no network here, no proprietary dataset, no integration treadmill, and the Figma converter that used to be the hard part is MIT on npm. What is left is real work all the same. The prompt below is one call to one model; the product is a chain of them, each step routed to whichever model is the right trade of quality against speed for that step, with the prompts behind each layer rewritten over months of iteration. None of that shows up on screen one. It shows up on screen nine, and in how many re-prompts it took to get there. The other thing money buys is a canvas, where a change is a click instead of a whole file re-prompted. Neither is a moat, and the prompt below is the honest test of what they are worth to you.
xclicking an element to change it instead of re-prompting the whole file
xa tuned multi-step pipeline · you get one prompt on one model, and you close the gap in your own iteration time
xsketch input · photograph a hand-drawn screen and get it back as a design
xshare links, project history, and team seats
Don't feel like building it? These folks already made it free.
no votes, no pay-to-list · just what's real
Sleek pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| free | $0/workspace | $0/workspace | 1 project and one-time credits for about 1 design; limited exports; credits do not renew. |
| starter | $24.99/workspace | $20/workspace | 3,000 credits/month, about 100 screens, and 5 projects. |
| pro | $49.99/workspace | $30/workspace | 20,000 credits/month, about 650 screens, and unlimited projects. |
| team | — | $40/user | 30,000 credits/seat/month, about 1,000 screens per seat, and unlimited projects. |
free tier1 project and one-time trial credits for about 1 design; credits never reset.
billingStarter/Pro monthly + annual; Team annual price published; refunds only within 24 hours
hidden costsA generation uses about 30 credits and edits use 5–30. Pro and Team can buy 5,000 non-expiring credits for $15; Starter cannot buy extra credits.
verified 2026-08-11 · source ↗
Is Sleek free?
The free plan is one project on one-time trial credits; nothing renews. Paid is Starter at $24.99/mo (checked 2026-08-07).
Vibecode Sleek
Kinda. The core of Sleek is buildable in a weekend with the prompt on this page, but there are real gaps: clicking an element to change it instead of re-prompting the whole file, a tuned multi-step pipeline · you get one prompt on one model, and you close the gap in your own iteration time. Read the honest list above before committing.
How much does Sleek cost?
Sleek costs about $24.99/month (Starter, checked 2026-08-07), which is $299.88 per year.
What do I lose by replacing Sleek?
Honestly: clicking an element to change it instead of re-prompting the whole file; a tuned multi-step pipeline · you get one prompt on one model, and you close the gap in your own iteration time; sketch input · photograph a hand-drawn screen and get it back as a design; share links, project history, and team seats. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Sleek?
Yes: Google Stitch (Prompt or screenshot to multi-screen UI, editable canvas, prototypes, Figma copy, and frontend code export; usage is capped, not trialled.) The prompt is for when you want it exactly your way.