A full-stack AI-powered tool that generates, refines, and replaces live HTML/CSS/JS websites from text prompts or image sketches — powered by Groq's ultra-fast LLM inference with a surgical patch-based refinement architecture and RAG-powered design inspiration.
| Feature | Details |
|---|---|
| Text → Website | Describe any site → full HTML/CSS/JS in 3–8 s via Groq |
| Section-aware output | LLM returns named sections (navbar, hero, features, pricing, testimonials, cta, footer) |
| Version History | Every generation stored; click any version to instantly restore |
| Style Controls | Palette (5 options) + Font (5 options, each label rendered in its own typeface) + Layout picker |
| Visually rich output | Mandatory system prompt rules: gradients, scoped CSS, real content, no lorem ipsum |
| Feature | Details |
|---|---|
| Surgical Refinement | Follow-up prompts via /refine — only changed sections returned and merged |
| Order-preserving merge | Merge iterates currentSections order as source of truth; never reorders |
| Section flash | Yellow outline flashes on changed sections after refinement |
| Refine fallback | If refinement parse fails → auto full regen, flagged in Langfuse |
| Sketch-to-Site (vision) | Upload wireframe/screenshot → 8-step analysis prompt replicates exact layout + colors |
| CSS scoping | scopeCSS() helper prefixes every rule with #section-{name} to prevent conflicts |
| Retry on parse fail | LLM calls retry once with JSON correction nudge before raising |
| Feature | Details |
|---|---|
| Design RAG | rag.py hardcoded vector store: 10 snippets covering hero, navbar, cards, pricing, testimonials, footer, forms, CTA, color palettes, mobile-first patterns |
| Keyword retrieval | retrieve_design_context(prompt) keyword-matches prompt against tags, injects top 2–3 CSS snippets into every LLM system prompt |
| Design quality uplift | Retrieved snippets give LLM concrete CSS patterns (gradients, spacing, hover states) to reference |
| Feature | Details |
|---|---|
| Langfuse tracing | Every call traced with mode, tokens, latency, section count, fallback flag |
| Exhaustive backend logging | [PROMPT_IN] [SYSTEM_PROMPT] [RAW_LLM_OUT] [PARSE_ATTEMPT] [PARSE_SUCCESS] [PARSE_FAIL] [SECTIONS_SENT] [REFINE_DIFF] [FALLBACK] [LANGFUSE_SENT] |
| Frontend debug logging | [REQUEST_SENT] [RESPONSE_RAW] [SECTIONS_RECEIVED] [MERGE_*] [ASSEMBLED_HTML] [CSS_SCOPED] |
| Debug Panel | ?debug=true in URL reveals collapsible bottom-left overlay with request/response/assembly stats + "Copy Assembled HTML" + "Copy System Prompt" |
| Iframe sandbox fix | allow-scripts allow-same-origin allow-forms allow-modals — Google Fonts and JS both work |
| Feature | Details |
|---|---|
| Ctrl+Enter / Cmd+Enter | Keyboard shortcut to submit prompt |
| Copy HTML button | In chat header — copies full assembled HTML to clipboard |
| Token count + time | Shown below every assistant message |
| Section-aware loading | Spinner labels differ for generate / refine / vision |
| Responsive layout | Works on tablet viewports (35% / 65% split, min-width guards) |
┌──────────────────────────────────────────────────────────────┐
│ Browser (User) │
└───────────────────────────┬──────────────────────────────────┘
│ HTTPS
┌─────────────────▼─────────────────┐
│ Vercel (Frontend) │
│ React 18 + Vite + Tailwind CSS │
│ │
│ ChatPanel │ PreviewPanel │
│ StyleControls VersionHistory │
│ DebugPanel (dev + ?debug=true) │
│ useWebsiteBuilder (hook) │
└──────┬────────────────┬────────────┘
│ POST /generate │ POST /refine
│ POST /generate-vision
┌──────▼────────────────▼────────────┐
│ Render (Backend) │
│ FastAPI v2.1 │
│ │
│ /generate ──► agent.py │
│ /refine ──► agent.py │ ──► Langfuse
│ /generate-vision ► agent.py │
│ /health ──► { status: ok } │
│ │
│ agent.py ──► rag.py (RAG inject) │
│ ──► Groq LLM (+ retry) │
└────────────────────────────────────┘
User prompt ──► POST /refine
│
├─► RAG retrieves design snippets → injected into system prompt
├─► LLM returns RefinementOutput
│ changed_sections: [hero, cta]
│ unchanged_section_names: [navbar, footer]
│
├─► mergeSections() — iterates currentSections order
│ replace changed, keep unchanged, never reorder
│
├─► assembleSections() — CSS scoped per section
│
└─► iframe updates, changed sections flash yellow
| Method | Path | Body | Returns |
|---|---|---|---|
GET |
/health |
— | { status: "ok" } |
POST |
/generate |
GenerateRequest (JSON) |
WebsiteOutput |
POST |
/refine |
RefineRequest (JSON) |
RefinementOutput |
POST |
/generate-vision |
multipart: prompt, image, style_preferences |
WebsiteOutput |
Add ?debug=true to any endpoint to receive debug_system_prompt in the response.
| Code | Meaning |
|---|---|
| 400 | Empty or whitespace-only prompt |
| 415 | Uploaded file is not a supported image type |
| 422 | LLM returned malformed JSON (after retry) |
| 503 | Groq API unavailable |
class SectionPatch(BaseModel):
section: str # "navbar" | "hero" | "features" | "cta" | "footer" | ...
html: str
css: str
js: str | None
class WebsiteOutput(BaseModel):
title: str
sections: list[SectionPatch]
full_html: str
full_css: str
full_js: str
generation_time_ms: float
token_count: int
debug_system_prompt: str | None # populated when ?debug=true
class RefinementOutput(BaseModel):
title: str
changed_sections: list[SectionPatch]
unchanged_section_names: list[str]
generation_time_ms: float
token_count: int
fallback: bool
debug_system_prompt: str | None- Python ≥ 3.12, Node.js ≥ 18, a Groq API key
cd backend
cp .env.example .env
# Edit .env — add your GROQ_API_KEY
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8000 --reloadSwagger UI: http://localhost:8000/docs
cd frontend
cp .env.example .env.local
# VITE_API_URL defaults to http://localhost:8000 if unset
npm install
npm run devApp: http://localhost:5173
Debug mode: http://localhost:5173/?debug=true
# Generate
curl -X POST http://localhost:8000/generate \
-H "Content-Type: application/json" \
-d '{"prompt":"a landing page for wireless earbuds"}'
# Generate with debug system prompt
curl -X POST "http://localhost:8000/generate?debug=true" \
-H "Content-Type: application/json" \
-d '{"prompt":"a SaaS landing page"}'
# Refine (surgical patch)
curl -X POST http://localhost:8000/refine \
-H "Content-Type: application/json" \
-d '{
"prompt": "make the hero section dark with neon accents",
"title": "EarPods Pro",
"current_sections": [
{"section":"hero","html":"<section id=hero><h1>EarPods</h1></section>","css":"#hero{background:#fff}"}
]
}'
# Vision (sketch-to-site)
curl -X POST http://localhost:8000/generate-vision \
-F "prompt=replicate this UI for earbuds" \
-F "image=@sketch.png"Backend logs — printed to stdout/stderr of the uvicorn process (terminal). In production on Render they appear in the Render dashboard → Logs tab. To persist to a file locally:
uvicorn main:app --host 0.0.0.0 --port 8000 --reload 2>&1 | tee backend.logAll log lines follow the format: TIMESTAMP [LEVEL] module — [TAG] message
Key tags: [PROMPT_IN] [SYSTEM_PROMPT] [RAW_LLM_OUT] [PARSE_SUCCESS] [PARSE_FAIL] [SECTIONS_SENT] [REFINE_DIFF] [FALLBACK] [LANGFUSE_SENT] [RAG] [RETRY]
Frontend logs — browser DevTools → Console tab.
Key tags: [WEBSITE_BUILDER] [REQUEST_SENT] [RESPONSE_RAW] [SECTIONS_RECEIVED] [MERGE_BEFORE/AFTER] [ASSEMBLED_HTML] [CSS_SCOPED] [JS_SECTIONS] [IFRAME_LOAD]
Langfuse traces — https://cloud.langfuse.com (requires LANGFUSE_PUBLIC_KEY + LANGFUSE_SECRET_KEY in .env)
-
Connect repo to Render — auto-detects
render.yaml -
Set environment variables:
Key Value GROQ_API_KEYgsk_…LANGFUSE_PUBLIC_KEYpk-lf-…(optional)LANGFUSE_SECRET_KEYsk-lf-…(optional)LANGFUSE_BASE_URLhttps://cloud.langfuse.comALLOWED_ORIGIN(set after Vercel deploy) -
Note the URL:
https://your-service.onrender.com
- Import repo to Vercel, set Root Directory =
frontend - Set
VITE_API_URL= your Render URL - Deploy → note Vercel URL → update
ALLOWED_ORIGINin Render → redeploy
hackathon/
├── backend/
│ ├── main.py # FastAPI: /generate, /refine, /generate-vision, /health
│ ├── agent.py # Groq client — generate, refine, vision + retry logic
│ ├── rag.py # RAG: 10 design snippets + keyword retrieval
│ ├── schemas.py # SectionPatch, WebsiteOutput, RefinementOutput, ...
│ ├── tracer.py # Langfuse tracing
│ ├── requirements.txt
│ └── .env.example
├── frontend/
│ ├── index.html # Preloads all 5 Google Fonts
│ └── src/
│ ├── App.jsx
│ ├── components/
│ │ ├── ChatPanel.jsx # Prompt input, Ctrl+Enter, Copy HTML, image upload
│ │ ├── PreviewPanel.jsx # iframe + sandbox fix + section flash
│ │ ├── MessageBubble.jsx # Chat bubbles with token/time metadata footer
│ │ ├── StyleControls.jsx # Palette / font (rendered in own typeface) / layout
│ │ ├── VersionHistory.jsx # Slide-in version drawer
│ │ └── DebugPanel.jsx # Dev-only debug overlay (?debug=true)
│ ├── hooks/
│ │ └── useWebsiteBuilder.js # All state, API routing, order-preserving merge
│ └── utils/
│ └── combineHTML.js # assembleSections (CSS scoping) + combineHTML
├── render.yaml
├── .gitignore
└── README.md
| Layer | Technology |
|---|---|
| LLM (text) | Groq — llama-3.3-70b-versatile |
| LLM (vision) | Groq — meta-llama/llama-4-scout-17b-16e-instruct |
| API | FastAPI + Pydantic v2 |
| RAG | In-memory keyword retrieval (rag.py) |
| Tracing | Langfuse |
| Frontend | React 18 + Vite + Tailwind CSS |
| Backend hosting | Render |
| Frontend hosting | Vercel |
| Limitation | Notes |
|---|---|
| Token budget | llama-3.3-70b-versatile at 8 000 tokens can truncate very large sites (6+ sections with heavy JS) |
| Vision model context | llama-4-scout may miss fine detail in complex screenshots; prompt explicitly asks for hex codes |
| RAG is keyword-only | No embeddings — retrieval degrades on abstract or metaphorical prompts |
| No streaming | Full response awaited before rendering; sites with many sections may feel slow |
| CSS scoping regex | scopeCSS() is regex-based and may mis-scope edge cases with deeply nested at-rules |
| Render cold start | Free tier Render instances sleep after inactivity — first request after idle takes ~30 s |
| No auth | API endpoints are open — add an API key header before production use |
What broke: JSON parsing was the biggest initial hurdle. Even with Llama 3.3, the model occasionally wrapped JSON in markdown fences or included preamble text, which broke the frontend. I solved this by implementing a robust regex-based
_extract_jsonhelper and a retry-with-correction loop that nudges the LLM if the first parse fails. CSS scoping also required several iterations to correctly handle@importand@keyframeswithout double-prefixing selectors.What surprised me: The sheer speed of Groq's Llama 3.3 70B model. It allowed me to move from a "full-regeneration" model to a "surgical-patch" model without the user feeling any lag. Also, the "Design RAG" approach—injecting raw CSS snippets into the prompt—had a much higher impact on design quality than complex prompt engineering alone.
If I had another week: I would implement streaming generation so sections appear one-by-one as they are generated. I'd also replace the keyword-based RAG with a proper vector database (Chroma/Qdrant) to allow for more nuanced design retrieval, and add a "Component Library" where users could drag-and-drop pre-generated blocks.
"A dark SaaS landing page for an AI writing tool called Quillify. Dark purple theme, Poppins font, hero section with gradient headline, 3-feature card grid, pricing table with a highlighted Pro plan, and a CTA with email capture."
This prompt hits: RAG (hero + pricing + CTA snippets retrieved), style controls (dark palette + Poppins font pre-selected), and all 6+ sections.
- Patch architecture: Show
/refinereturning only changed sections — not a full page reload - RAG injection: Open backend logs → show
[RAG]line listing which design snippets were retrieved - CSS scoping: Open DevTools Elements tab → show
#section-hero .hero-titlescoped selectors - Debug panel: Add
?debug=true→ show live HTML stats + copy assembled HTML - Prompt Management: Open Langfuse Dashboard → show the versioned prompts being fetched remotely by the backend.
"Make the hero section neon green on black, like a hacker terminal"
Shows only hero section flashing yellow (section-level patch), other sections unchanged.
- Streaming generation with per-section progressive rendering
- Component library RAG with real Figma/Tailwind component embeddings
- Export to GitHub — push generated site as a deployable repo
- Multi-page generation — generate linked pages with shared navbar/footer