BoilerPrompt

Bolt prompts

Describe an app, get a running full-stack project in your browser tab.

Try Bolt(20% off)

34 prompts, every one free to copy. Jump to: App, Backend, Design, Features, Frontend, Refactor, Testing

What Bolt is good at

Bolt (bolt.new) is StackBlitz's AI app builder. Your whole project runs inside a WebContainer, a Node environment in the browser tab, so the dev server, npm installs, and terminal all execute right there with no local setup. You describe an app, Bolt scaffolds the files, and a live preview sits next to an editable file tree, so you can fix code by hand mid-conversation. It fits developers who want a working full-stack starting point in minutes and are comfortable reading generated source. Against its neighbors: v0 focuses on React UI components you export into an existing project, and Lovable leans hard on Supabase-backed apps with less file-level exposure. Bolt sits between them, producing the entire project, frontend, server routes, and config, while keeping every file open for direct edits. Deploys go out through its Netlify integration, and persistent data usually means connecting Supabase, since the in-browser runtime resets between sessions.

How to prompt Bolt

Put the whole spec in your first message. Bolt meters usage by tokens and big course corrections trigger wholesale rewrites, so a complete opening brief (pages, data model, behaviors, states, styling) is cheaper than five rounds of "actually, also add...".

Name the stack explicitly. Left to defaults you will usually get a Vite and React project; if you want Next.js, Vue, or plain HTML, say so in the first line.

Decide persistence up front. The WebContainer cannot run Postgres or other native binaries, so state whether data lives in memory, in localStorage, or in Supabase through Bolt's integration. Skip this and you may get a mock layer you did not want.

Ask for seed data. The live preview is the whole point of Bolt; populated lists and charts let you judge output at a glance, while empty screens hide layout bugs.

Scope follow-ups tightly. Say which file or feature to change and add "do not modify other files." When the preview breaks, paste the exact terminal or console error rather than describing the symptom; Bolt fixes precise errors far more reliably than vague ones.

End prompts with a verification step: what to click in the preview and what should happen when you do.

All 34 Bolt prompts

App 5 prompts

Build a SaaS starter app for a product that helps small teams manage {{resource}}. Stack: React with TypeScript, Supabase for auth and application data, Stripe stubbed behind a feature flag rather than integrated.

Pages:
- Public landing: hero, three feature sections, pricing table with Free, Pro, and Team tiers, and a footer
- Sign up and log in with email plus password, and a magic-link option
- Protected app shell: dashboard home, a {{resource}} section with create, edit, and delete, a team page with invite-by-email, and settings with profile and workspace tabs
- Billing page with the current plan and an upgrade button calling a clearly marked placeholder checkout
- A dedicated {{addon}} page in the protected shell with its own sidebar entry. Give it a real empty state explaining the feature before any data exists and gate its actions by role like the rest of the app; if it touches payments or billing, keep it behind the same flag as the Stripe stub. Done means the sidebar link, the route, and the empty state all render in a fresh workspace.

Data model: workspaces, users, and memberships with roles (owner, admin, member), plus {{resource}} records scoped to a workspace. Every query filters by workspace id, enforced in one data-access module rather than scattered through components.

Behaviors:
- Unauthenticated visitors hitting app routes get redirected to login, then back to their intended destination
- Only owners and admins see invite and billing actions
- Optimistic UI on {{resource}} create and delete, rolling back on failure

Empty and error states: a fresh workspace shows an onboarding checklist; failed logins distinguish a wrong password from an unconfirmed email.

Styling: marketing polish on the public pages, a denser utilitarian layout inside the app, one primary color shared by both.

Do not implement real payment processing. Seed one demo workspace with sample records so the app is browsable right after the first sign-up.
Build a to-do app as a Vite + React project with Tailwind in this Bolt workspace. Single screen, three zones: an input row pinned to the top, a filter bar with All, Active, and Done, and the task list.

Data model: task with id, title, due_date, done, created_at. Persist to localStorage under the key bolt_todos so the list survives a preview refresh. No backend for v1.

Behaviors:
- Enter in the input adds a task, an empty input does nothing and shows no alert.
- Checkbox toggles done, completed rows get strikethrough and sink below open ones.
- Double click a title to edit inline, Escape cancels, blur saves.
- Each row has a small calendar button that reveals a native date input, picking a date sets due_date, clearing it removes the date, and the chosen date renders next to the title.
- A live footer count in the form 3 of 8 open.
- Clear completed asks for confirmation before deleting.

Edge cases: duplicate titles allowed, titles trimmed of surrounding whitespace, a due_date earlier than today renders its date text in red on open tasks only, never on completed ones. If localStorage throws, fall back to in-memory state and show a small banner explaining that tasks will not persist.

Empty state: the message Nothing here, add your first task, with focus already in the input.

Styling: neutral grays, one accent color shared by the add button and the active filter, system font stack, content column capped at 640px and centered.

File layout: App.tsx for composition, TaskItem.tsx for a row, useTasks.ts for all state and persistence, nothing else, so the whole app stays readable in Bolt's editor in one sitting.

Acceptance: add three tasks, give one a due date of yesterday and confirm its date shows in red, complete one, refresh the preview, and confirm the due date, the filter counts, and the completed ordering all survive the reload.
Build a blog with a small self-hosted CMS in one Bolt project: public site plus an /admin area, Vite + React, connected to Supabase for storage and auth.

Data model, created as a Supabase migration:
- posts: id, title, slug, excerpt, body_md, cover_url, status (draft or published), published_at, author_id.
- tags: id, name, slug, plus a post_tags join table.
Enable row level security: anyone can select published posts, only authenticated authors can insert or update their own rows.

Public screens:
- / : published posts newest first, excerpt and tag chips, paginated 10 per page.
- /post/:slug : renders body_md as sanitized HTML, code blocks in monospace, and a 404 view for unknown or draft slugs.
- /tag/:slug : the same list filtered by tag.

Admin screens behind Supabase email auth:
- /admin : a table of all posts with status badges and edit links.
- /admin/edit/:id : title, slug auto-generated from the title but editable, a markdown textarea with side-by-side preview, and separate Save draft and Publish buttons. Publishing sets published_at once and never overwrites it on later edits.

Edge cases: slug collisions get a numeric suffix, deleting a tag detaches it from posts rather than deleting them, and an empty admin list shows a Create your first post button.

Styling: a reading measure around 65 characters, generous line height, no sidebar on the public site.

Acceptance: sign in through the preview, publish a post, open it by slug while logged out, and confirm drafts stay hidden from anonymous visitors.
Build a small e-commerce store for selling {{resource}} in this Bolt project: Vite + React + Tailwind, Supabase for the catalog, cart kept client-side.

Data, as Supabase migrations with seed data:
- products: id, name, slug, description, price_cents, currency, image_url, stock, active.
- orders: id, email, total_cents, status, created_at, plus order_items with product_id, qty, unit_price_cents.
Seed 8 products so the preview is browsable immediately.

Screens:
- / : product grid with image, name, price, and a sold-out overlay when stock is 0.
- /product/:slug : gallery placeholder, quantity stepper capped at available stock, Add to cart.
- /cart : line items with qty editing and removal, subtotal recomputed from freshly fetched product rows, never from values stored in the cart itself.
- /checkout : email and shipping form with inline validation, and a Place order button that writes the order and its items to Supabase in one RPC call, then shows a confirmation with the order id. Payment is a stub marked TODO, do not fake a card form.

Cart rules: persists in localStorage under cart_v1, merges duplicate lines, and drops items whose product went inactive while telling the user which ones were removed.

Empty and error states: an empty grid says the store has no products yet, the empty cart links back to the grid, and a failed order write keeps the form filled and shows a retry banner.

Money: always integer cents, formatted at render, no floating point arithmetic anywhere.

Acceptance: add two items, refresh the preview and confirm the cart survives, place an order, then open Supabase's table view and find the order row with its items.
Build a booking app in this Bolt project: a public page where clients pick a slot, and an owner view for availability. Vite + React, Supabase for data and auth, every timestamp stored UTC.

Data model as Supabase migrations:
- services: id, name, duration_min, buffer_min, active.
- availability_rules: id, weekday, start_time, end_time.
- bookings: id, service_id, starts_at timestamptz, client_name, client_email, status, plus a unique index on (service_id, starts_at) so double booking fails at the database, not just in the UI.

Public flow at /book:
1. Pick a service from a card list showing duration.
2. A week view of open slots computed server-side from availability rules minus existing bookings minus buffer time, rendered in the visitor's local timezone with the timezone name printed under the grid.
3. Confirm with name and email, inline validation, then a confirmation screen with a cancel link carrying a signed token.

Owner view at /admin behind Supabase auth: a weekly calendar of bookings with click-to-cancel, and an availability editor of weekday rows with add and remove time ranges. Overlapping ranges merge on save with a notice.

Edge cases: past slots never render, a slot taken between page load and confirm returns a slot_taken error and refreshes the grid, cancelled bookings free their slot immediately, and slot generation gets checked across a daylight saving boundary week.

Empty states: an owner with no availability sees a setup prompt, and no active services hides the public flow behind a Coming soon card.

Acceptance: book a slot in the preview, open a second tab, try booking the same slot, and confirm the unique index rejects the duplicate.

Backend 5 prompts

Build a REST API in Node with TypeScript, using Express, for managing {{resource}} records, backed by {{database}}.

The in-browser container runs Node only, never a database server. If {{database}} is Postgres, MySQL, MongoDB, or Redis, read a connection string from an env var and point the data layer at a hosted instance. Without one, fall back to a JSON file store behind the same interface so the preview still works.

Endpoints:
- GET /api/{{resource}} with pagination (page and limit query params, default limit 20) and a total count in the response body
- GET /api/{{resource}}/:id, returning 404 with a JSON error body when the id does not exist
- POST /api/{{resource}} with request body validation; reject unknown fields and return 422 with per-field messages
- PATCH /api/{{resource}}/:id for partial updates, validating only the fields present
- DELETE /api/{{resource}}/:id, returning 204 on success and 404 for a missing id
- GET /api/health returning uptime, which store is active, and, when hosted, whether the database connection is alive

Data model: each record has an id, name, description, status (active or archived), createdAt, and updatedAt, plus two or three extra fields that fit a {{resource}}.

Behaviors:
- One JSON error envelope for every non-2xx response: { error: { code, message, details } }
- Request logging middleware printing method, path, status, and duration to the terminal
- CORS enabled so the preview page can call the API
- Seed 15 realistic records on startup so list responses are never empty

Also serve a plain HTML page at the root route documenting every endpoint with an example request, plus a form to POST a new record and buttons that hit each endpoint and print the raw JSON.

Structure the code as a server entry, routes, validation, and data access modules. After scaffolding, start the server and confirm /api/health and the list endpoint both return 200.
Create a Node project running GraphQL Yoga so the server starts inside Bolt's WebContainer and the preview pane serves the GraphiQL playground at /graphql.

Schema, SDL-first in schema.graphql: a {{resource}} type with id, name, status, and createdAt. Queries: a paginated list taking limit, offset, and an optional status filter, plus a single fetch by id. Mutations: create, update, and delete for {{resource}}, each returning the affected record, delete included, not a boolean.

Resolvers live in src/resolvers.ts, one file, no codegen for v1. Back them with an in-memory array seeded with 12 records so the playground is useful immediately, but isolate every read and write in src/store.ts so I can swap in {{database}} later without touching a resolver.

Rules:
- Reject an empty name with a GraphQLError carrying extensions.code BAD_USER_INPUT.
- Clamp limit to 50 silently.
- Unknown id: null from the single query, a NOT_FOUND error from mutations.

Add GET /healthz returning { ok: true } outside GraphQL, and print the playground URL to the terminal on boot.

Dependencies: graphql and graphql-yoga only, no Apollo, no subscriptions, nothing with native bindings since WebContainers cannot compile them.

Acceptance: from GraphiQL in the preview I can create a record, page through the list with offset, filter by status, update a status, delete the record, and get NOT_FOUND when deleting it a second time.
Add JWT auth to the Express API in this Bolt project. Use jsonwebtoken and bcryptjs, not bcrypt, because native bindings will not compile inside the WebContainer.

Endpoints in server/auth.ts:
- POST /api/auth/register: email plus password, hash with bcryptjs at 10 rounds, reject passwords under 8 characters with 422 and a field-level error body.
- POST /api/auth/login: verify credentials, return a 15 minute access token in the JSON body and a 7 day refresh token in an httpOnly cookie.
- POST /api/auth/refresh: read the cookie, rotate the refresh token, return a new access token. A reused old refresh token invalidates the whole session family.
- POST /api/auth/logout: clear the cookie and revoke the refresh token server-side.

Middleware requireAuth in server/middleware.ts: read the Authorization Bearer header, verify signature and expiry, attach req.user, and return 401 with { error: "token_expired" } distinct from { error: "invalid_token" } so the client knows when to refresh.

Secrets: JWT_SECRET comes from .env. Generate a placeholder value and remind me to change it, never hardcode it.

Apply the middleware to the existing /api/{{resource}} routes, leaving GET public. Persist users and refresh tokens in whatever store the project already uses, adding a users table if none exists.

Acceptance: write scripts/auth-check.sh with curl calls that register, log in, hit a protected route, tamper with one token character and get 401, refresh successfully, then log out and prove the refresh path is dead.
Design and apply a Postgres schema through Bolt's Supabase connection. Deliver it as ordered SQL files in supabase/migrations, one concern per file, so I can read each in the editor before it runs.

Migration 1, core tables for a {{resource}} tracker:
- profiles: id uuid referencing auth.users, display_name, created_at.
- {{resource}}s: id uuid default gen_random_uuid(), owner_id referencing profiles, title text not null with a length check between 1 and 200, status as a proper enum type (draft, active, archived), created_at and updated_at timestamptz defaulting to now().
- comments: id, {{resource}}_id with on delete cascade, author_id, body.

Migration 2, integrity and speed:
- A trigger function touch_updated_at applied to every table carrying updated_at.
- Indexes on owner_id, on comments by parent and created_at descending, and a partial index on status where status is active.
- A unique constraint on (owner_id, title), with a SQL comment explaining the choice.

Migration 3, row level security:
- Enable RLS on all three tables.
- Owners get full access to their rows, authenticated users can select active {{resource}}s, and comments are readable wherever the parent is readable. Each policy carries a one-line comment stating what it permits.

Rules: no serial columns, uuid keys everywhere, no nullable booleans, every timestamp timestamptz, and never store money or quantities as float.

Finish with a smoke check I can paste into the Supabase SQL editor: insert a profile and a record, update it to verify updated_at moves, then attempt a cross-owner update and confirm RLS rejects it.
Build a file upload endpoint in this Bolt project's Express server, storing files in Supabase Storage, because the WebContainer filesystem is wiped between sessions and must never be the system of record.

Route: POST /api/uploads in server/uploads.ts, multipart/form-data with field name file, parsed by multer using memory storage so nothing lingers on disk.

Validation, in this order, each with its own error code:
- 413 file_too_large past 10 MB, enforced through multer limits so oversized bodies abort early.
- 415 unsupported_type unless the magic bytes say png, jpeg, webp, or pdf. Detect with the file-type package, never trusting the client mimetype or the extension.
- 400 missing_file when the field is absent.

Naming and storage: build the object key as uploads/{year}/{month}/{uuid} with the extension taken from the detected type, and keep the original filename only as metadata after stripping path separators and control characters. Upload to a private bucket, then return 201 with id, key, size, contentType, and a short-lived signed URL.

Companion routes: GET /api/uploads/:id returns metadata plus a fresh signed URL, and DELETE removes both the object and the metadata row. Metadata lives in an uploads table with uploader_id taken from the auth middleware.

Failure behavior: if the Storage call dies mid-request, return 502 storage_unavailable and log the attempted key. Insert the metadata row only after the upload succeeds so no half-written records exist.

Acceptance: from Bolt's terminal, curl a small png and get 201, rename an .exe to .png and get 415, send an 11 MB file and get 413, then open the signed URL in the preview and see the image.

Design 14 prompts

Create a hero section for {{product}} in this Bolt project as its own file: src/components/Hero.tsx, imported at the top of App.tsx above the existing content. Touch no other files.

This hero is center-aligned and runs on a strict height budget so it fits a small laptop screen: total rendered height under 720px, so nothing scrolls at 1280x800. Allocate roughly 40px for the chip row, 170px for the headline, 60px for the subhead, 56px for the buttons, 48px for the proof row, and 280px for the visual, plus 64px top and 48px bottom padding. If you run over, trim the visual first.

Contents, all centered in one max-width column:
- Status chip: a small bordered pill naming the category of {{product}}.
- Headline: up to ten words with exactly one keyword set in the palette's accent — that keyword is the thing {{audience}} most wants.
- Subhead: two short sentences totalling about 25 words — what it does, then who it is for, naming {{audience}} directly.
- Buttons: a solid primary labelled "{{cta}}" beside an outlined secondary for viewing an example.
- Proof row: an overlapping avatar cluster plus a caption counting teams or users.
- Visual: one wide screenshot-placeholder card, capped at 280px tall.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Responsive: under 640px the buttons stack full-width, the avatar cluster drops to three, and the visual switches to a 4:5 center crop.

Use the instant preview while building — narrow the preview pane for the mobile checks. When done, confirm: (1) no scrollbar appears inside an 800px-tall preview, (2) only Hero.tsx plus one App.tsx import line changed, (3) the accent keyword survives line wrapping intact, (4) the buttons stack correctly at 375px wide.
Add a social-proof section — a testimonial wall — to the {{product}} landing page in this Bolt project, positioned between how-it-works and pricing. Desktop gets a static wall {{audience}} can read without interacting; only mobile degrades to a carousel.

Project wiring: create src/components/TestimonialWall and src/data/testimonials.ts. The data module exports twelve entries ({quote, name, role, company, featured?}) plus a logos array; the component renders whatever the data contains, so the wall grows by editing one file. Keep an eye on the instant preview as you go.

Render order inside the section:
1. An h2 stating the aggregate proof — "[N] teams shipped faster with {{product}}" territory; derive the claim from the product and keep it falsifiable.
2. Featured pull-quote: the single entry flagged featured: true, set much larger than body text, attribution on its own line.
3. The wall: the remaining eleven entries in a masonry arrangement (CSS columns is fine) across three columns, cards at natural height. Do not equalize heights — the ragged edge is what makes it read as a wall.
4. Logos strip: one row of reduced-contrast marks with an aria-label naming the companies.
5. A right-aligned link reading "{{cta}}" as the section's exit action.

Copy rules for the twelve quotes: written for {{audience}}, at least five must contain a number or timeframe, none may exceed 45 words, and every attribution needs all three of name, role, and company.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Spacing: 6rem section padding; 3rem between the featured quote and the wall; 1.5rem column gap. Under 768px, hide the multi-column wall and show a scroll-snap carousel of the same cards — one per screen, swipe-driven, never autoplaying.

Verify in the preview: cards show ragged masonry heights on desktop; exactly one featured quote renders; five or more quotes contain numbers; the mobile carousel snaps and never advances on its own; deleting an entry from testimonials.ts removes a card without breaking layout.
In the current Bolt project, extend the {{product}} landing page with a FAQ accordion section, slotted between pricing and the footer. The audience is {{audience}}; the section's job is to clear the last objections standing between them and the final CTA.

Implementation: create src/components/FaqSection plus src/data/faqs.ts exporting an array of {question, answer} pairs, and render the accordion from that array so editing copy never means editing markup. Use native <details>/<summary> only if you also enforce single-open behavior with a small controller script; otherwise build button-driven rows where each button sets aria-expanded and toggles its named panel — one open at a time, so opening row N closes whichever row was open. Arrow-key navigation between question buttons is required, not optional.

Content: eight entries derived from {{product}}'s sales objections for {{audience}}, split into two groups with small labels — "Before you start" (pricing, setup time, migration, trial terms) and "Once you're in" (data ownership, limits, support, cancellation). Questions under twelve words; answers 40–70 words with one verifiable specific in each.

Anatomy top to bottom: an h2 in the "answers before you ask" register, reworded for {{product}}; the two labelled groups of rows; then a final strip with the text "Something we missed?" and a solid button reading "{{cta}}".

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Spacing: 6rem section padding, 2.5rem between the two groups, rows separated by 1px rules with 1.25rem of internal padding. Mobile: groups stay stacked and question text wraps without pushing the chevron off its row.

Use the live preview to confirm: opening any row closes the previously open one; both group labels render; keyboard arrows move between questions; the "{{cta}}" strip sits after the second group; the data file drives all eight rows.
Set up a portfolio one-pager for {{product}} as a project I can iterate on in the live preview. File structure: index.html, css/site.css, js/site.js, and data/projects.js exporting an array of six project objects (title, blurb, result, tags, imagePath). The work grid must render from that array, so editing my work never touches markup.

Page anatomy:
1. Identity hero — the name as the dominant line, then one positioning sentence telling {{audience}} exactly what they'd hire this person for; a small scroll cue at the section's bottom edge.
2. Work grid — render the six projects from data/projects.js into a three-column card grid; hover lifts a card 4px and fades the result line in over the image; each card is a link stub.
3. About band — one first-person paragraph, 60 words max, covering experience and approach; beside it a short facts column (based in, years, tools).
4. Skills row — six plain-text capability chips; no progress bars, no percentages.
5. Contact block — heading, one sentence inviting a specific kind of project, the email link, and two profile links.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Spacing and breakpoints:
- Sections separated by 7rem on desktop, 4rem below 768px; grid gap 1.5rem.
- Below 900px the grid runs two columns; below 600px, one, with the hover reveal swapped for a result line always visible under the image.

The primary call to action is "{{cta}}" — use it verbatim as the contact block's email link text.

Check in the preview when done: editing a title in data/projects.js changes the grid on reload; the grid renders 3, 2, then 1 columns at 1200, 800, and 500px widths; no image is stretched off its ratio; and nothing overflows horizontally at 375px.
Scaffold a runnable waitlist splash project for {{product}} that I can watch live in the preview while we iterate. Keep the structure flat: index.html, src/style.css, src/main.js, and src/waitlist.js with all form logic isolated so the storage layer can be swapped later. Front end only for now — use localStorage as the stand-in store.

Build in this order:
1. Hero — a small-caps label with a launch-date placeholder, a headline framing the pain {{product}} removes for {{audience}}, a one-line subhead, then the capture form.
2. Proof band — a horizontal strip of three items: an avatar cluster with a joined count, a one-sentence founder note signed with a name, and a blurred screenshot placeholder captioned "shipping at launch".
3. The form, full treatment — label, email input, submit; invalid input shakes the field and prints a message under it; success collapses the form into a card reading "You're #N in line", N read from the stored count, plus an invite link promising a five-spot jump per referral.
4. FAQ — four details/summary elements, zero JS: launch window, launch pricing, email policy, referral mechanics.
5. Footer — one row: name, year, contact link.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Spacing and breakpoints:
- 6rem between sections on desktop, 4rem under 768px; internal gaps step 0.5rem / 1rem / 2rem only.
- Under 640px the proof band stacks vertically and the input and button both go full width.

The primary call to action is "{{cta}}" — the submit button reads exactly that.

After building, confirm in the preview: a refresh keeps the queue number (localStorage); an invalid email can never reach the success state; each FAQ item opens without console errors; and the hero plus form fit a 375px screen with no horizontal scroll.
Spin up a fresh Vite + React + Tailwind project and build the marketing page for {{product}} in it, aimed squarely at {{audience}}. Structure the repo so I can maintain it after export: src/sections/ holds one file per section, src/components/ holds shared pieces (Button, Container, SectionHeading), and src/content.ts holds every string on the page so copy edits never touch markup.

Render the sections in this order inside App:
1. Header — translucent bar that gains a solid background after 40px of scroll; logo, four links, CTA button.
2. Hero — H1 built from {{product}}'s core verb, object, and outcome; one-sentence subhead; the CTA; and a stat line of three inline numbers with labels directly beneath.
3. Proof — one customer quote under 30 words with name and role, instead of a logo wall.
4. Features — 2x2 grid of cards; each card is an icon, a bolded claim, and one supporting sentence. No "learn more" links.
5. Showcase — large product screenshot in a perspective-tilted container that straightens as you scroll.
6. Pricing preview — one featured plan card next to a short list of what every plan includes, plus a link to full pricing.
7. FAQ — six items using native details/summary elements, two columns on desktop, one on mobile.
8. CTA banner, then footer.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Spacing and breakpoints:
- One spacing scale only (16/24/40/64/96px); section padding 96px on desktop, 56px under 768px.
- The feature grid collapses to a single column below 640px; the stat line wraps to two rows rather than truncating.

The primary call to action is "{{cta}}", and it must be visible without scrolling on a 1366x768 viewport.

Verify in the live preview before finishing: the header transition fires at the scroll threshold; every visible string is imported from content.ts; the details elements open and close without layout shift; the hero CTA sits above the fold at 1366x768 and again at 390px width.
Scaffold a complete, runnable project in Bolt for the agency site of {{product}}, written for {{audience}}. Use Vite + React with no backend, one file per section under src/sections/ — Manifesto.jsx, Work.jsx, Services.jsx, Method.jsx, People.jsx, Footer.jsx — imported in that order by App.jsx, so I can watch each piece land in the instant preview.

Structure (deliberately manifesto-first, not a conventional hero):
1. Manifesto — open with a 3-line declaration of how {{product}} approaches its specialty, set at the largest size on the page; beneath it, one line stating who the studio serves and a scroll cue.
2. Work — 5 projects as full-bleed alternating rows (media area swapping left/right each row) with client, sector, and a one-number result; keep row heights consistent.
3. Services — a 3-column band of capability groups, 4 short line items in each column.
4. Method — 4 stages on a horizontal timeline with a connecting rule; each stage names the artifact the client receives.
5. People — founders and leads in a single row of cards: name, role, one-line bio.
6. Footer — an oversized email link, the city, and social links on one line.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Spacing and breakpoints:
- Define --section-gap in a global stylesheet: 7rem desktop, 4rem under 768px; every section reads the variable instead of hard-coding margins.
- Under 768px, Work rows stack media-over-text and the Services band becomes a single column.

Write real placeholder copy derived from {{product}}'s specialty — sector-specific project names, never "Project One". The primary call to action is "{{cta}}", appearing once as a fixed corner button that smooth-scrolls to the Footer.

Once it runs in preview, confirm: App.jsx imports all six section files; Work rows alternate sides and stack correctly on narrow widths; the corner "{{cta}}" button scrolls to the footer; the preview console shows no errors; every project row displays a number.
In Bolt, scaffold a runnable single-product landing page for {{product}}, sold to {{audience}}. Vite + React, no backend; all product data lives in src/data/product.js (name, price, images, specs, reviews) so copy edits never touch markup. Components in src/components/: Gallery, PurchasePanel, Benefits, SpecTable, Reviews, AssuranceBar, StickyBar. I'll follow along in the live preview.

Page anatomy:
1. Gallery and PurchasePanel side by side. The gallery is a main image with a thumbnail rail; the panel holds the name, a one-sentence promise, a rating summary, the price, a quantity stepper, the buy button, then three assurance lines with icons — dispatch time, returns window, warranty.
2. Benefits — 4 compact cards in a 2x2 grid, each pairing an outcome headline (6 words maximum) with two supporting sentences; no icon-only cards.
3. SpecTable — a two-column table rendered from the specs in product.js; include dimensions and compatibility rows relevant to {{product}}.
4. Reviews — a score header plus 5 reviews rendered from data, each with name, date, stars, and text; a summary line states the share of 5-star ratings.
5. AssuranceBar — guarantee, secure payment, and support hours in one banded row before the footer.
6. StickyBar — slides up once the PurchasePanel leaves the viewport (IntersectionObserver), holding the price and the buy button.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Rhythm and breakpoints:
- Global spacing tokens in CSS: --gap-section at 6rem desktop, 3.5rem under 768px; every component reads the variable.
- Under 768px: thumbnails move below the main image as a swipe row, the 2x2 benefit grid becomes one column, and the StickyBar is the permanent mobile purchase point.

Label every purchase button "{{cta}}". Write review and benefit copy that reflects what {{product}} actually does for its buyer — no lorem ipsum anywhere.

Once the preview runs, confirm: editing the price in product.js updates both buy locations; the StickyBar appears and disappears at the right scroll positions; the spec table renders every row from data; the console shows no errors; mobile presents a single-column flow.
Create a standalone 404 page for {{product}} in this project — one file at src/pages/NotFound plus a catch-all route entry, styled to feel deliberate rather than default. Its job: tell {{audience}} they're off the map and hand them a way back within five seconds.

Structure, one full-viewport column, vertically centered:
1. Status line — small uppercase label reading "Error 404 — page not found", so the state is explicit before any humor starts.
2. Headline — one memorable sentence riffing on {{product}}'s domain (ask yourself what "lost" means inside this product's world); ten words maximum.
3. The interactive touch — a fake command line beneath the headline that types out "searching for page... 0 results" once on load, then blinks a cursor; typing text and pressing Enter routes to the search page with the query prefilled.
4. Recovery actions — the primary button plus two secondary links: homepage and support.
5. Baseboard — a thin strip at the bottom naming the last-visited page (read document.referrer; hide the strip when it's empty) with a "take me back" link.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Spacing and responsive:
- Center the column with flex and cap content width at 560px; vertical gaps run 16px within a group and 48px between groups.
- Under 480px, skip the typing animation and render the command line's final state statically, so small devices avoid the animation and any keyboard-trap weirdness.

The primary call to action is "{{cta}}".

Watch the preview and verify before finishing: the typing animation runs exactly once and never loops; pressing Enter with typed text navigates to /search?q= plus the text; the referrer strip is hidden when the page is opened directly; there is no vertical scrollbar at 1440x900; the column still centers at 360px wide.
In the Bolt project that already contains the landing page for {{product}}, add one new section — pricing — between the social-proof band and the footer. Keep it a section inside the page flow; do not create a /pricing route.

Files: put the markup in src/components/PricingSection (matching whatever framework the project scaffolded) and the tier data in src/data/pricing.ts as an array of three objects — name, blurb, monthly, yearly, features, buttonLabel — so copy edits never touch markup. Import the section into the existing page file.

Inside the section, top to bottom:
1. Heading pair: an h2 naming the value {{product}} charges for, plus a subhead that tells {{audience}} which tier to pick ("Most teams start on ...").
2. Billing toggle bound to one piece of state; yearly shows the discounted number with the monthly-equivalent struck through.
3. Three tiers rendered from the data file. Mark the middle object recommended: true and render that card with a badge and a stronger outline. Its button is the only filled one and reads "{{cta}}"; the outer tiers get outline buttons with labels from the data file.
4. Feature bullets: the first tier lists its complete set; each higher tier opens with "Everything in the previous plan, plus —" followed by at most four new items, each phrased as a capability, not a limit.
5. Under the grid, one reassurance line (trial length or cancellation terms) in muted text.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Spacing: section vertical padding about 6rem, 1.5rem card gap, and a fixed price-block height so bullets align across cards. Mobile: single column, recommended tier first, toggle centered above.

Watch the instant preview while iterating and confirm: prices flip with the toggle; one filled button total; bullets align across the three cards at desktop width; the reassurance line renders under the grid; the footer below is unaffected.
Build a bento features section for {{product}} in Bolt, structured as data plus renderer so I can edit content without touching layout: create src/data/bento.ts exporting a typed array of cell objects (title, body, span, kind, href) and src/components/BentoSection.tsx that maps over it. Import the section into App.tsx after the hero. No other files change.

Six cells on a 12-column desktop grid, 16px gaps — spans in column units:
1. kind "showcase", span 6, double row height: the headline capability of {{product}} with a large media placeholder; body copy of two lines maximum.
2. kind "stat", span 3: one number that matters to {{audience}}, set huge, with a six-word caption.
3. kind "feature", span 3: icon, four-word title, one sentence.
4. kind "feature", span 3: same shape, different feature — no overlapping claims between cells.
5. kind "stat", span 3: a second metric in a different unit than cell 2.
6. kind "action", span 6, short row: a horizontal card with a one-line invitation and a button labelled "{{cta}}" — the only button in the section.

Section chrome: an eyebrow and an H2 of seven words maximum above the grid, then 40px down to the first row. Section padding: 112px top, 96px bottom.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Responsive: spans re-map at 768px to a 6-column grid (showcase full width, everything else half), and to a single column under 480px with the action cell kept last.

Check in the instant preview at all three widths: (1) no cell wraps its span into an orphan row that leaves a gap, (2) editing a title in bento.ts hot-reloads the content with zero layout change, (3) the two stat units differ, (4) the action button is the section's only button. Finally confirm only the three named files changed.
In this Bolt project, create the footer for the {{product}} landing page as its own file — src/components/Footer.tsx, or the equivalent for the current stack — and import it at the end of the page so the instant preview shows it the moment it compiles. Footers written inline at the bottom of a page file get neglected; a separate file keeps this one honest.

Contents, in order:

First, a brand block and newsletter capture side by side on desktop: wordmark, a two-line description of {{product}} written for {{audience}} (what it is, then who it serves), and an email form whose label promises a specific artifact — not "subscribe to our newsletter". The form is secondary; under it, add a plain link repeating "{{cta}}" for anyone who reached the bottom unconverted.

Second, the link architecture: derive 3-4 column headings from the shape of {{product}} — a tool gets Product / Docs / Company, a service gets Services / Work / Company, a content site gets Topics / About / Follow. Real destinations only; where a URL is unknown, leave a TODO comment instead of inventing a route.

Third, a hairline-separated legal strip: copyright, privacy, terms, and social icons inline at text size.

Fourth, optionally, the product name set enormous behind the strip, clipped at the bottom edge, faint enough to skim past.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Spacing and breakpoints:
- Top padding about 1.5x the gap between mid-page sections, so the footer reads as an ending rather than another block; 4rem brand row to columns, 3rem to the legal strip.
- Below 768px: the brand block and form stack first, columns go 2-up, and the legal strip wraps with icons on their own line.

Check in the preview: the footer lives in its own imported file, no invented URLs exist (TODOs instead), the email input and button align on one line at desktop width, columns hold 2-up on narrow viewports without overflow, and the clipped wordmark never scrolls the page sideways.
Add an animated background to the hero of my {{product}} project in Bolt. Create one new file, src/components/HeroBackground.tsx, and mount it inside the existing hero as the bottom layer. Do not modify the hero's copy, buttons, or spacing, and change no other file beyond the single import.

Wiring: the hero container gets position relative and overflow hidden; HeroBackground renders absolute and full-bleed with pointer-events none, under a content wrapper with a higher z-index. That is the whole integration surface.

Pick one technique to match the visual direction: blurred gradient blobs drifting on CSS transforms for soft directions, a canvas particle drift for dark or technical directions, or slow-rotating wireframe geometry for editorial and brutalist directions. Whichever you pick, it should register with {{audience}} as atmosphere, not as a feature.

Engineering rules, because Bolt hot-reloads constantly: any rAF loop lives in a useEffect whose cleanup cancels the frame and removes listeners — otherwise hot module reload stacks duplicate loops and the preview crawls. Cap particles at 60 and devicePixelRatio at 2. Animate transform and opacity only. A prefers-reduced-motion query must skip the loop entirely and paint one static frame.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Motion is the intensity dial: element count, drift distance, and loop speed scale together, and the calm end should be barely perceptible.

Legibility: keep a 72px calm margin around the headline block, add a scrim if the effect brightens, and confirm the primary button reading "{{cta}}" holds full contrast at every point in the loop.

Responsive: under 640px, halve the element count and lengthen the loop; on phones the effect should nearly disappear.

Verify in the instant preview: (1) frame rate stays smooth after three consecutive hot reloads — the duplicate-loop test, (2) reduced-motion emulation shows a still frame, (3) every hero control still clicks with the layer mounted, (4) only the new file and one import changed.
Scaffold a feature grid section for the {{product}} landing page in this Bolt project as two files: src/data/features.ts exporting an array of six {icon, title, benefit} objects, and src/components/FeatureGrid.tsx that maps over it. Import it into the page right after the hero. Keeping copy in a data file means editing text never risks the layout, and the instant preview reflows as you tune each line.

Layout contract — a grid, not a bento:
- All six cells identical in span and height, three columns at desktop, no featured card, no mixed tile sizes. A bento trades uniformity for emphasis; this section presents six capabilities as equally load-bearing, scannable in two sweeps.
- Equal height must survive uneven copy: grid-stretched cards, no fixed pixel heights.

Filling the data file — the benefit formula: for each of six features of {{product}}, write a title (3 words max, the capability) and a benefit (12 words max, the payoff for {{audience}}). Test every benefit by prefixing "so you can" — if the sentence doesn't parse, it's a description, rewrite it. The words "seamless", "powerful", and "robust" are banned.

Icon discipline: import all six icons from one set in a single statement; render at one size with one treatment — tinted chip or plain glyph, chosen once. If no sensible icon exists, use a neutral geometric mark over a mismatched literal one.

Above the grid: an eyebrow, a headline promising the summed outcome, and a one-sentence subhead. Below it, a centered link labeled "{{cta}}" — the section's only interactive element besides card hovers.

Hover: one effect on the card root, reversible in under 200ms.

Visual direction:
{{style}}

Palette: {{palette}}
Typography: {{type}}
Motion: {{motion}}

Rhythm and breakpoints:
- Section vertical padding around 6rem; 3.5rem header to grid; 1.5rem gap; 1.5rem card padding.
- Below 1024px: two columns. Below 640px: one column, header block still within the section's first screen.

Check in the preview: six equal-height cells at all three breakpoints, every benefit passes the "so you can" test, icons uniform in size and treatment, exactly one hover effect present, and the data file compiles with no unused imports.

Features 4 prompts

Add a search feature to this app so users can find {{resource}} records by name and description.

Behavior:
- A search input in the header, focusable with the / key, with a clear button
- Debounce input by roughly 250ms so matching does not run on every keystroke
- Case-insensitive matching on name and description; treat multiple words as AND terms
- Highlight the matched substring in each result using a mark element, not custom spans with inline styles
- Show a result count, and when nothing matches, an empty state suggesting a spelling check or clearing filters
- Sync the query to the URL as ?q= so results survive a reload and can be shared; restore the input from the URL on load
- Keyboard support: arrow keys move through results, Enter opens the selected record, Escape clears and closes

Implementation:
- If the data is already client-side, filter in a memoized selector and do not mutate the underlying list
- If records come from an API, add a q parameter to the list endpoint and match server-side, keeping only the debounce in the browser
- Keep the logic in one module (src/lib/search.ts or equivalent) as a pure function I can unit test: given records and a query, return matches with their positions

Edge cases: an empty query shows the normal list, whitespace-only queries count as empty, queries containing regex characters like ( or * must not crash the matcher, and clearing the search restores the prior scroll position.

Finish by demonstrating three searches in the preview: a term with hits, a term with none, and a term containing a special character. Then list the files you changed.
Add a chat assistant to the existing app in this Bolt project without restructuring it.

Server side: server/chat.ts, an Express route POST /api/chat that proxies to the LLM provider. Read the key from LLM_API_KEY in .env, keep .env out of version control, and never let the key reach the client bundle. Stream the reply back as server-sent events so tokens render as they arrive. A missing key returns 503 with { error: "missing_key" } instead of crashing the WebContainer process. Disable compression middleware on this route so SSE chunks flush.

Client side: a ChatPanel component docked to the right edge, collapsible. Parts: message list, a textarea growing to four lines, a send button disabled while a reply streams. Send the last 20 messages as context and drop older ones.

States:
- First open shows a short hint describing what the assistant can help with in this app.
- Streaming shows a cursor at the end of the incoming message plus a Stop button that aborts the fetch and keeps the partial text.
- Provider errors and rate limits render inline in the transcript with a Retry link, and the user's draft stays in the box.
- Offline disables send with a tooltip saying why.

Persist the transcript to sessionStorage so a preview reload mid-test does not wipe the conversation.

Log one line per request on the server with model, token counts if the provider returns them, and latency.

Acceptance: with a key in .env, ask a question and watch tokens stream in the preview, hit Stop halfway and confirm the partial reply stays, then remove the key and confirm the 503 path renders as an inline error.
Add transactional email notifications to this Bolt project. Use {{addon}} as the provider, called through its HTTP API from the server only, because outbound SMTP from a WebContainer is unreliable and the key must never reach the client bundle.

Structure:
- server/email/client.ts: one send(to, template, data) function, key read from EMAIL_API_KEY in .env, throwing a typed EmailError on any non-2xx response.
- server/email/templates/: welcome.ts, password-reset.ts, weekly-digest.ts, each exporting a subject and an HTML body built from plain template strings, no templating library. Inline all CSS, use tables for layout, and include a plain-text alternative per template.

Triggers: hook welcome into the existing signup flow, password-reset into the reset request, and expose POST /api/notifications/test that sends any template to my own address so I can proof designs from the preview.

Reliability:
- Sends are fire-and-forget from the request path, pushed onto an in-process queue with three retries and exponential backoff, so a provider outage never blocks signup.
- Log every attempt with status sent, retried, or failed.
- If EMAIL_API_KEY is unset, print the fully rendered email to the terminal tagged SIMULATED instead of failing, so dev flows still complete.

Edge cases: strip newlines from user-supplied names to block header injection, lowercase recipient addresses, and refuse obviously invalid addresses before calling the API.

Acceptance: hit the test endpoint with no key and read the SIMULATED render in Bolt's terminal, then add a real key and receive the welcome email in an actual inbox.
Add CSV import and export for {{resource}} records to this Bolt project.

Import is a three-step modal launched from an Import button on the {{resource}} list:
1. Upload: drag and drop or file picker, .csv only, files over 5 MB rejected with a clear message before parsing. Parse in the browser with PapaParse in streaming mode so the preview pane never freezes on big files.
2. Map columns: show the first five rows in a table, with a dropdown per CSV column mapping it to a {{resource}} field or Skip. Auto-match by header name, case-insensitive. If a required field is unmapped, disable Next and say which one.
3. Validate and commit: run every row through the same validation the normal create form uses. Show counts of valid and invalid rows, offer a downloadable errors.csv containing only the failed rows plus a reason column, then import valid rows in batches of 100 with a progress bar. Cancel between batches keeps rows already written.

Import rules: trim cells, treat empty strings as null, accept dates as either YYYY-MM-DD or DD/MM/YYYY and report which format was detected, and resolve duplicates by natural key with one Skip or Overwrite choice applied to the whole file.

Export: a button honoring the list's current filters, served as GET /api/{{resource}}/export.csv from the Node server with a Content-Disposition header, values escaped per RFC 4180, and any cell starting with =, +, -, or @ prefixed with a single quote to block spreadsheet formula injection.

Acceptance: round-trip it, export the seed data, re-import that same file, and finish with zero invalid rows and zero duplicates created.

Frontend 4 prompts

Build a responsive admin dashboard UI for monitoring {{resource}} activity. Use React with TypeScript and Tailwind.

Layout: a collapsible sidebar with navigation groups (Overview, {{resource}}, Reports, Settings) and active-route highlighting, plus a top bar with search, a date-range selector, and a user menu.

Screens:
1. Overview: four stat cards (total records, active this period, change versus the previous period computed from the mock data, and open alerts), a line chart of the last 30 days, a bar chart grouped by category, and a recent activity feed.
2. {{resource}} table: sortable columns, a text filter, a status dropdown, pagination, and a detail drawer that opens on row click.
3. Settings: a simple preferences form persisted to localStorage.

Data: create src/data/mock.ts, a module that generates records with a seeded deterministic generator so charts and tables look identical on every reload. No backend calls; components read through a thin data-access layer I can later point at a real API.

States: skeleton loaders on first paint, an empty state with a clear-filters action when nothing matches, and an error boundary around each chart so one failure cannot blank the whole page.

Styling: neutral palette with a single accent color, an 8px spacing scale, cards with subtle borders instead of heavy shadows. Everything must work down to 375px wide, with the sidebar collapsing to a hamburger menu on mobile.

When you are done, check the preview at desktop and mobile widths and fix any horizontal overflow before declaring the task finished.
Build a landing page for a developer tool as a Vite + React + Tailwind project, ready to deploy to Netlify straight from Bolt.

Sections, top to bottom, each its own component in src/sections/:
1. Hero.tsx: one-line value proposition, a subline, primary CTA Start free, secondary Read docs, and a terminal-style block showing an install command with a copy button.
2. Features.tsx: three cards, each an icon, a bold claim, two sentences. No carousel.
3. HowItWorks.tsx: a numbered three-step row that stacks vertically under 768px.
4. SocialProof.tsx: a grayscale logo strip using placeholder SVGs I can swap, and no invented testimonial quotes.
5. FAQ.tsx: five items using native details and summary elements, no JavaScript accordion.
6. Footer.tsx: links, a license note, a mailto.

Behaviors: a fixed header fades in past 400px of scroll, anchor links smooth-scroll to sections, the copy button flashes Copied for two seconds, and the CTA scrolls to a signup form that validates email format inline rather than with a browser alert. Wire the form to a console.log stub marked TODO for the real endpoint.

Constraints: system font stack only, no external font or image requests, every asset inline SVG or local so the deployed page makes zero third-party calls. One h1 on the page, alt text on every image, real button elements for every action. No animation library, CSS transitions only.

Acceptance: list the files created, then confirm in Bolt's preview that the page holds together at 1280px, 768px, and 360px without horizontal scrolling.
Build a pricing page as src/pages/Pricing.tsx in this Bolt project, Tailwind for styling, no new dependencies.

Layout: three tier cards, Free, Pro, Team, in a single row that stacks vertically under 700px. Pro gets a highlighted border and a Most popular tag, and all cards stay equal height regardless of feature list length.

Above the cards, a monthly and annual toggle implemented as a real radio group, not two styled divs. Annual view shows per-month pricing with a small billed yearly note and a savings badge computed from the two numbers in config, never hardcoded as a percentage string. If the computed savings is zero or negative, hide the badge.

All plan data lives in src/pricing.config.ts: name, monthly, annual, tagline, features, CTA label, CTA target. The page renders entirely from that file so prices change without touching JSX.

Feature lists: a checkmark for included items, excluded items in muted gray reading Not included with no icon. Any feature needing context gets an info icon whose tooltip opens on focus as well as hover.

Below the cards, a comparison table built from the same config, sticky first column, horizontal scroll contained inside its own wrapper on small screens. Then three billing questions in native details elements.

Edge cases: a zero price renders Free instead of $0, a tier missing an annual price hides its toggle-dependent block, and currency goes through Intl.NumberFormat.

Acceptance: flip the toggle and confirm every price, badge, and CTA updates from config alone, then narrow Bolt's preview to phone width and verify the page never pans horizontally.
Build a reusable data table in this Bolt project: src/components/DataTable.tsx plus a useSort.ts hook, TypeScript, Tailwind, no table library.

API: the component takes rows and a columns array, each column being { key, header, sortable, type, align, render }. Everything else stays internal.

Sorting behavior:
- Clicking a sortable header cycles ascending, descending, then unsorted, and the third click restores the original row order exactly.
- The active header shows a direction arrow and sets aria-sort on the th so screen readers announce it.
- String columns compare with localeCompare, numeric columns numerically, date columns by timestamp, chosen by the column's type field, never by sniffing cell values.
- Null and undefined cells sink to the bottom in both directions.
- Sorting is stable, so equal values keep their relative order.

Mechanics: a sticky header row inside a scrollable container with a fixed max height, zebra striping, hover highlight, and the container owning overflow-x so wide tables scroll inside themselves instead of stretching the page in Bolt's preview.

States: a loading prop renders skeleton rows matching column widths, an empty prop shows a message centered across all columns, and a cell whose render function throws shows an error glyph instead of unmounting the table.

Demo: wire it to a demo page with 200 generated rows including accented names, negative numbers, mixed-case strings, and some null dates, because those inputs expose most comparator bugs.

Acceptance: in the preview, sort each column both directions, confirm accented names order sensibly, nulls stay last either way, and the third click returns the seed order untouched.

Refactor 1 prompt

Audit and fix accessibility in this Bolt project without changing visual design or breaking existing behavior. Work in passes and show me a diff summary per pass instead of one giant rewrite.

Pass 1, semantics: replace clickable divs with button or anchor elements, ensure exactly one h1 per route, heading levels never skip, lists use ul and li, and nav, main, and footer landmarks exist.

Pass 2, keyboard: everything interactive reachable with Tab in a sensible order, a visible focus ring using outline-offset so layout never shifts, Escape closes any modal or dropdown, and focus returns to the trigger element on close. Add a skip-to-content link as the first focusable element.

Pass 3, forms and labels: every input gets a label element or aria-label, errors are announced by wiring aria-describedby to the error text, required fields carry aria-required, and no placeholder-as-label pattern survives.

Pass 4, contrast and motion: flag any text below 4.5:1 against its background and propose the nearest passing shade from the existing palette, then wrap animations in a prefers-reduced-motion media query.

Rules: no accessibility overlay library, no aria roles on elements that already carry them implicitly, and never remove an existing test id.

Acceptance: I will tab through the app in Bolt's preview pane with the mouse untouched, so when you finish, state the expected focus order per screen and list every file changed, grouped by pass.

Testing 1 prompt

Go through this project and add consistent error handling in three layers, without changing any feature behavior.

1. UI layer: wrap each route-level component in an error boundary that renders an inline fallback with a retry button, never a blank screen. Form submissions must show field-level validation messages plus a summary at the top of the form when a request fails. Every async button gets a loading state and is disabled while a request is in flight, to prevent double submits.

2. Data layer: centralize fetch calls in one module if they are not already. Normalize failures into a typed AppError with a kind (network, validation, auth, not_found, server), a message, and the original cause. Map HTTP statuses to those kinds. Retry idempotent GET requests once on network failure; never retry mutations automatically.

3. Reporting: add a small logError helper that logs structured errors to the console in development, with a clearly marked hook where a real error-tracking service can plug in later. Do not add a third-party dependency for this.

Specific cases to cover: offline (failed fetch), a 401 that redirects to login exactly once instead of looping, a 404 on a detail page rendering a not-found view with a link back to the list, and a JSON parse failure from a malformed response.

Before finishing, prove each path in the preview: add a dev-only panel with buttons that force every error kind through the real code paths, then tell me which files you changed. No empty catch blocks anywhere; if a catch exists only to ignore an error, add a comment explaining why that is safe.

Prompting patterns that work in Bolt

Whole spec, first message

Write the complete brief before you hit enter: screens, data model, behaviors, empty states, styling direction. Bolt regenerates aggressively when scope shifts, so late additions cost more than early ones. Treat the opening prompt like a ticket you would hand a contractor.

Persistence declaration

State where data lives in the first prompt: in memory, in localStorage, or in Supabase via the integration. The browser runtime resets between sessions, so undeclared persistence becomes whatever Bolt guesses, usually a mock you will rip out later.

Seeded preview

Ask for realistic seed records and deterministic mock generators so every list, chart, and detail view renders populated in the preview. You cannot judge a dashboard from an empty table, and layout bugs hide behind blank states.

Error-paste debugging

When the preview goes blank or the terminal prints a stack trace, copy the exact text into chat instead of describing it. Bolt resolves a pasted TypeError in one turn; "the page is broken" leads to guessing and unrelated rewrites.

Common mistakes

One-line prompts, then steering

Starting with "build me a CRM" and refining over ten messages burns tokens and accumulates half-rewritten files. The fix is boring: write the full spec first, then keep each follow-up to a single feature.

Expecting a real database in the container

Bolt cannot run Postgres, Redis, or other native services inside the browser runtime. Prompts that assume them either stall or get silently faked. Specify Supabase through the integration, or accept in-memory data that resets.

Letting small asks trigger big rewrites

A request to change one button can come back with three rewritten files. Constrain scope explicitly, name the file, and lock anything you have hand-edited so the agent cannot clobber your changes.

Prompts for other tools