HTML Wizard
AI-powered factory for reusable HTML/CSS/JS components. Describe a widget in plain English and get a single self-contained envelope file — one inline stylesheet, one ES6 module, one #root mount point — rendered live in a sandboxed iframe, captured to PNG, and refined iteratively with real vision and console feedback.
HTML Wizard Documentation
# 🧩 HTML Wizard
> **Describe a widget in plain words — get one self-contained HTML file that actually runs, a PNG of how it looks, and a console log of how it behaves.**
HTML Wizard is the component-shaped sibling of [SVG Wizard](../svg-wizard) and [TeX Wizard](../tex-wizard).
Instead of compiling LaTeX or authoring vectors, it writes **`component.html`** — a strict
*single-file envelope* — mounts it in a **sandboxed iframe** (so the JavaScript really executes),
captures **`console.log.md`** from the live run, and rasterises the rendered DOM to **`render.png`**
using a 100 % browser-based snapshot (`<foreignObject>` → `<canvas>` → PNG).
Both artefacts are fed back to a vision model for an iterative
*mount → look → critique → fix* loop.
---
## 📦 The envelope format
Every component is **one file**, portable by copy-paste, with **no external resources**:
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Star Rating</title>
<style>
/* exactly ONE style block — tokens on :root, component styles scoped to #root */
:root { --brand: #5b8dff; }
body { margin: 0; font-family: system-ui, sans-serif; }
#root { display: inline-flex; gap: 4px; padding: 16px; }
#root .star { cursor: pointer; font-size: 28px; color: #444; }
#root .star[aria-checked="true"] { color: var(--brand); }
</style>
</head>
<body>
<div id="root" data-component="star-rating"></div>
<script type="module">
// exactly ONE module block — modern ES6+, no imports, no external URLs
function mount(root, props = {}) {
const { max = 5, value = 3, onChange = () => {} } = props;
/* … build DOM, wire events … */
return { update(next) {/* … */}, destroy() {/* … */} };
}
globalThis.component = { mount }; // reusable entry point
mount(document.getElementById("root")); // demo instance for the preview
</script>
</body>
</html>
```
The built-in **linter** enforces this: exactly one `<style>`, exactly one
`<script type="module">`, a `#root` element, a `mount(root, props)` entry point,
no `http(s)://` `src`/`href`, no remote `import`, no inline `on*` handlers.
---
## 🚀 Getting Started
### Step 1 · Set up your inputs 📝
1. **Notes** — what the component does, its props, states, behaviour and a11y needs.
2. **Design Directives** *(optional)* — palette, spacing scale, radii, typography, dark-mode rules.
3. **Models** — Smart model (authoring), Fast model (edits), Image model (visual review).
### Step 2 · Run the pipeline ⚙️
| # | Step | What it does |
|---|------|--------------|
| 1 | **▶ Render HTML** | Generates `component.html` from `notes.md` + `style.md`. |
| 2 | **📸 Mount & Capture** | Mounts the envelope in an iframe, records console output → `console.log.md`, snapshots the live DOM → `render.png`, writes `render.log.md`. |
| 3 | **✏️ Update HTML** | Applies `update-notes.md` to `component.html`. |
| 4 | **🔍 Review Render** | Vision model inspects `render.png` + `console.log.md` and rewrites `update-notes.md`. |
Then loop **4 → 3 → 2** until it looks and behaves right. Tick **Auto-capture** and every code change re-renders instantly.
### Step 3 · Inspect & export 📤
* **Live preview** re-mounts as you type (debounced) — real DOM, real events, real JS.
* **🔎 Lint** validates the envelope contract at any time.
* **⬇ Download** / **📋 Copy** the HTML, **⬇ Download PNG** for the raster.
---
## 🎛 Capture Options
| Option | Meaning |
|--------|---------|
| **Viewport** | Width of the preview iframe (360 – 1280 px). Height auto-grows to the content. |
| **Scale** | PNG is `viewport × scale`. Total pixels capped (~16 MP) and clamped automatically. |
| **Background** | Transparent keeps alpha; White/Dark composites a backdrop — useful for contrast checks. |
| **Settle delay** | How long to wait after load before snapshotting (lets async JS finish). |
| **Live preview** | Re-mount the iframe while you type. |
| **Auto-capture** | Re-capture PNG + console whenever `component.html` changes. |
---
## 💡 Pro Tips
* **Keep it self-contained.** External `<img src="https://…">` taints the canvas and breaks the PNG export — inline `data:` URIs instead.
* **External references are stripped.** During capture any non-`data:` `src`/`href`/`url()` is inlined if fetchable, otherwise dropped (and reported in `render.log.md`) so the canvas stays exportable.
* **Fonts:** only fonts already available to the browser render. Prefer generic stacks — `@font-face` with a remote URL is rejected by the linter.
* **Snapshot limits:** the PNG is taken via `<foreignObject>`, so `<canvas>` pixels, `<video>` frames and nested iframes are **not** captured. The linter warns when it sees them.
* **Errors are gold:** anything thrown at mount time lands in `console.log.md` and the review step will fix it.
* `Ctrl/Cmd+S` saves every editor at once; autosave also runs as you type.
* If your session's file API rejects binary uploads, the app writes `render.png.base64.txt` instead and warns you in the capture log — use **⬇ Download PNG** to keep a copy.
---
## 📂 Session files
| File | Role |
|------|------|
| `notes.md` | Your brief (input) |
| `style.md` | Design directives (input) |
| `update-notes.md` | Revision instructions (written by review, read by update) |
| `component.html` | The generated single-file envelope |
| `render.png` | Browser snapshot of the mounted component |
| `render.log.md` | Capture report: envelope lint, sizes, scale, warnings |
| `console.log.md` | Runtime console/error output from the live mount |
Happy componentising with **HTML Wizard**! 🧩✨