Turn an underspecified request into a clean, native-feeling HTML form by filling in one JSON spec — no form HTML by hand, no backend, no build step. The reviewer fills it out in the browser and clicks Copy for Claude; you get back a structured, agent-ready payload.
Answering steps through one question at a time; the sidebar tracks status live, and every question can be skipped or flagged. Try it live: www.bobspunt.com/intake-form/docs/demo.html — one self-contained file showing every question type. Or open examples/question-type-catalog.html locally.
Agents do their worst work when the request is vague. The usual fix is a wall of clarifying questions in chat, which is slow, easy to lose, and hard for a person to answer carefully. The other failure mode is just as bad: an agent (or a developer) hand-writing form HTML, which drifts, duplicates state across views, and quietly breaks.
Intake Form is the small thing in between. You describe the questions as a JSON spec; a single bundled renderer builds the whole form — both a step-by-step wizard and an all-sections view — from that one source. The human answers in a calm, well-designed UI, and the export comes back as a tidy block an agent can act on directly.
It came out of agent workflows: before starting ambiguous work, an agent generates a form, the human fills it in, and the answers flow back as structured context instead of a half-remembered chat thread.
Anyone who wants a person's input captured structured and unambiguous before work begins — without standing up a survey tool, a database, or a login.
- Scoping agent work. An agent hits 2+ real gaps, generates a form, and waits for a grounded brief instead of guessing.
- Lightweight requirements gathering. Drop a form into any project folder, send the file, get a clean export back.
- Decision intake. Capture the frame of a decision (reversibility, stakes, deciders, constraints) in one pass.
The reviewer clicks Copy for Claude and the renderer auto-formats every answer into a labeled export — no custom code:
=== INTAKE EXPORT ===
--- How to read this ---
1. Free-text NOTE lines carry more signal than the selections they
annotate. Where a note and a selection conflict, trust the note.
2. SKIPPED is information, not absence. It means the question missed —
do not re-ask it verbatim. Check the reason.
3. FLAGGED questions were judged wrong by the respondent.
REVERSIBILITY: Easily reversible
REVERSIBILITY_NOTE: reversible on paper, but the migration is one-way
CONSTRAINTS: Time | Compliance
BUDGET: SKIPPED (Doesn't apply)
EFFORT: 30 [UNTOUCHED DEFAULT — not confirmed by respondent]
FORMAT: Report
FORMAT_FLAG: Too specific / in the weeds — ask what the decision is first
REPORT_PAGES: 10
--- Context already known ---
User: a senior engineer on a small product team.
--- Routing suggestion ---
ask.reversibility -> route to a structured decision workflow ...
--- Response quality ---
1 question(s) skipped.
1 question(s) flagged as mis-targeted.
Treat these as evidence about the form, not only about the respondent.
When the reviewer says the form itself is wrong, a FORM_CRITIQUE block leads the
export and tells the consuming agent to regenerate rather than proceed.
There is no install step for the form itself — it's a template plus a renderer.
As an agent skill (Claude Code, Codex, or any SKILL.md-aware agent). Drop this folder into your skills directory (e.g. ~/.claude/skills/intake-form for Claude Code, ~/.agents/skills/intake-form for Codex). The agent reads SKILL.md, writes a JSON spec, and builds a form into your project.
As a Claude Code plugin.
/plugin marketplace add spunt/intake-form
/plugin install intake-form@intake-form
By hand. Write a JSON spec (schema in SKILL.md) and build it with the bundled CLI:
node tools/build.mjs my-spec.json --out my-form.htmlThe default --assets inline embeds ifbase.css and ifbase.js, so my-form.html is one self-contained file that opens anywhere and survives being emailed — see examples/question-type-catalog.html for the result. A malformed spec (unknown question type, duplicate id, bad theme) fails at build time with a clear message instead of a broken form in the browser. No Node? Edit template.html directly — replace the JSON inside <script id="form-spec"> and open it beside the skill files.
- One spec, two views.
ifbase.jsreads a single<script id="form-spec">block and builds both the wizard and the grouped layout from it, keeping state in sync. There is no second copy of the questions to drift out of sync — the structural fix for the classic "duplicate the DOM and one copy goes empty" bug. - Native controls. Real
<input>/<textarea>/range elements, keyboard-navigable, with an inference box, a commentary field on every closed-choice question, and Skip + Flag controls on every question. - Nothing is pre-selected. A guess the user waves through would be indistinguishable from an answer they chose, so the agent's hypothesis appears as a visible badge to accept or reject — never as a checked box.
- The form can tell you it is wrong. Any question can be skipped (with a reason) or flagged, and a form-level critique panel reachable from every question exports a
FORM_CRITIQUEblock telling the consuming agent to regenerate rather than proceed. - Sidebar navigation. A persistent table of contents lists every section and question with live status — answered, skipped, unanswered, flagged — and jumps to any of them.
- Cited material stays reachable. Attach
sourcesto the form or any question; they render as links that open in a new tab, so following a citation never discards the reviewer's answers. - Themeable from the spec. A single
--if-*OKLCH token layer drives color, type, motion, and density. Set athemeblock (preset,hue,palette, …) in the spec; no per-form CSS. Five presets ship:default,editorial,terminal,kraft, andstudio— all pass the accessibility audit. - Client-side only. Nothing is sent anywhere. Answers live in the page until the user clicks Copy for Claude or exports.
radio · checkbox · text · textarea · scale · slider · segmented · priority-rank (drag-reorder) · file-upload (base64-embedded) · narrative-card (non-input story beat) · embedded-media · plus per-option multi-branch follow-ups. Every type is demonstrated in examples/question-type-catalog.html; the full schema and authoring rules are in SKILL.md.
The renderer is backed by tooling in tools/: build.mjs (the authoring CLI, with validateSpec rejecting malformed specs before render), render-test.mjs (headless Playwright render → screenshot + console-error capture + export capture against golden files in test-specs/), axe-audit.mjs (programmatic accessibility audit on both views), and reach-test.mjs (reachability gate: asserts every theme preset is actually selectable and every question — display-only types included — has a navigation entry). Every shipped question type and theme preset has a golden export captured under test-specs/.
The reachability gate exists because render, golden, and axe checks all verify rendering and none verify reachability — twice a feature shipped complete in CSS, schema, docs, and goldens while remaining unselectable in the UI, passing every gate.
Current state: 13/13 specs render with zero console errors, and zero axe violations across both layouts and all five theme presets. The design decisions behind v1.1.0 — no pre-selection, conditional escape hatches, labeled scales, position instead of percentage — rest on published survey-methodology and WAI-ARIA guidance, summarized per change in CHANGELOG.md.
cd tools && npm install
node render-test.mjs ../test-specs/demo-all.json
node axe-audit.mjs ../test-specs/demo-all.jsonMIT © 2026 Bob Spunt. Use it in anything, including commercial work. Keep the copyright notice.
