Skill Overview
Skill Description
Copy this into the Description field when adding the skill to Claude.
Builds a fully functional Typeform-style one-question-at-a-time form as a production-ready .tsx file AND a standalone HTML file, deployable on any website — Framer, Webflow, Squarespace, Wix, Wordpress, vibe-coded sites, custom React apps, or anywhere else. Use this skill whenever someone asks to build a custom form, application form, lead gen form, quiz, survey, qualification funnel, or typeform clone. Trigger even if they say "form", "apply", "quiz", "survey", "funnel", or "questions" — especially if they mention Framer, TSX, React, Webflow, Squarespace, Wix, WordPress, or Google Sheets integration. Always use this skill proactively when a multi-step form or application flow is requested, even if the word "typeform" is never used.
How to Add This Skill to Claude
Skills live inside Projects. Create one if you don't have one: click Projects → + New Project.
Inside your project, click Set project instructions (the pencil icon).
Copy everything from the Skill Prompt section below and paste it into the instructions box. Click Save.
"Build me a Typeform-style application form for my coaching program"
Skill Prompt
Copy everything below and paste it into your Claude project instructions.
name: typeform-tsx
description: >
Builds a fully functional Typeform-style one-question-at-a-time form as a
production-ready .tsx file AND a standalone HTML file, deployable on any
website — Framer, Webflow, Squarespace, Wix, Wordpress, vibe-coded sites,
custom React apps, or anywhere else. Use this skill whenever someone asks to
build a custom form, application form, lead gen form, quiz, survey,
qualification funnel, or typeform clone. Trigger even if they say "form",
"apply", "quiz", "survey", "funnel", or "questions" — especially if they
mention Framer, TSX, React, Webflow, Squarespace, Wix, WordPress, or Google
Sheets integration. Always use this skill proactively when a multi-step form
or application flow is requested, even if the word "typeform" is never used.
# Typeform TSX Skill
Generates a complete, embed-ready form by interviewing the user, then
outputting both a .tsx (React/Framer) and a .html (works anywhere) file,
plus easy-to-follow Google Sheets setup instructions.
---
## Phase 1 — Interview
Ask ALL of the following using the ask_user_input_v0 tool. Do not skip any.
### Round 1 (always ask first)
1. What is this form for?
(application, lead gen, survey, quiz, onboarding, feedback, other)
2. What is your brand name?
3. What theme? (dark / light)
4. Primary accent color?
(hex code or description — e.g. "electric blue", "gold")
5. Where will this form live? (select all that apply)
- Framer
- Webflow
- Squarespace / Wix / Wordpress
- Custom / vibe-coded website (HTML)
- React app (not Framer)
- Not sure yet
### Round 2 (always ask second)
6. Does this form qualify or disqualify people?
(yes — send qualified to Calendly/URL | no — just collect data)
7. If yes: what is your Calendly or redirect URL?
8. Do you want data sent to Google Sheets? (yes / no / later)
9. Should the form have a welcome screen? (yes / no)
### Round 3 — Questions
Provide this template and wait for the user to fill it in:
QUESTION 1
Type: contact-info | short-text | long-text | email | multiple-choice | yes-no | number
Text: The question text?
Options (if multiple-choice): A) ... B) ... C) ...
Logic:
- If A → go to Q3
- If B → END (disqualify)
- Else → go to Q2
QUESTION 2
...
Rules to tell the user:
- No branching? Write Logic: none
- Early exit? Write → END (qualified) or → END (disqualified)
**Wait for the user to paste their questions before writing any code.**
---
## Phase 2 — Plan (internal only)
Before writing code, map out:
1. Step type union — every numeric step + outcome strings
2. Answers interface — one key per stored answer
3. useState fields — one per text/textarea input
4. Logic map — routing rules per choice, disqualify triggers
5. Sheet key map — answer key → Google Sheet column name
6. Theme tokens: bg / surface / border / accent / accentDim / text / textMuted
7. Deploy targets — which files to generate based on Round 1 Q5
---
## Phase 3 — Files to Generate
Always generate both:
File A — [BrandName]Form.tsx
React component. Works in Framer, React apps, Next.js, Vite, etc.
File B — [BrandName]Form.html
Single self-contained HTML file. No build step, no dependencies.
Works on ANY website via iframe embed or direct hosting.
The HTML file is a direct port of the TSX — same logic, same styling,
same validation, same Google Sheets integration — just vanilla JS.
Save both to /mnt/user-data/outputs/ and present both with present_files.
---
## Phase 3a — TSX Code Generation Rules
### Structure
1. imports (useState, useEffect, useRef only)
2. Step type union
3. Answers interface
4. CONFIG block (BRAND_NAME, THEME tokens, CALENDLY_URL, SHEET_URL, ABSTRACT_API_KEY)
5. Sub-components: Choice, OkButton, TextInput, FieldBlock, ErrMsg, Hint
6. Main export default function
- state: step, animDir, visible, field values, sel, err, phoneChecking
- goTo(), pick(), isValidPhoneFormat(), checkPhoneReal()
- validator functions
- sendToSheet() — EXPLICIT key mapping only
- keyboard useEffect
- slideStyle + qText
- JSX: progress → main → slides → bottom nav
### Styling rules (TSX)
- ALL styles inline — no CSS files, no Tailwind
- minHeight: "100vh" on root — NEVER height: "100vh"
- No position: absolute on active slides — position: relative when active
- No overflow: hidden anywhere
- Slide transition: opacity + translateY, 280ms, out then in
- Progress bar: 4px, accent fill, smooth transition
- Bottom nav: border-top, step counter, up/down arrows
---
## Phase 3b — HTML File Generation Rules
The HTML file must be 100% self-contained — one file, no imports, no npm,
no build step. It must work when opened directly in a browser or dropped into
any website as an iframe.
HTML-specific rules:
- Use const SHEET_URL = "YOUR_APPS_SCRIPT_URL_HERE" and
const ABSTRACT_API_KEY = "YOUR_ABSTRACT_API_KEY" at top of script
- Slides: use CSS classes .slide, .slide.active, .slide.exit toggled via JS
- body { min-height: 100vh; display: flex; flex-direction: column; }
- No height: 100vh, no overflow: hidden
- Phone validation: checkPhoneReal is async, show "Checking..." on OK button
- sendToSheet(): identical explicit key mapping as TSX version
- Keyboard: document.addEventListener('keydown', ...) — Enter to advance
- Font: load Inter via Google Fonts link in head
---
## Phase 4 — Shared Logic Rules (applies to BOTH files)
### Validation
- contact-info: name non-empty → phone format → phone real check → email regex
- text/long-text: non-empty
- multiple-choice: selection required, OK disabled until picked
- Clear error on every advance
### Phone Validation (both files)
Layer 1:
function isValidPhoneFormat(raw) {
const digits = raw.replace(/[\s\-().+]/g, "")
return /^\d{7,15}$/.test(digits)
}
Layer 2:
async function checkPhoneReal(phone) {
// Skip API check entirely if no key is configured — never block users with a placeholder key
if (!ABSTRACT_API_KEY || ABSTRACT_API_KEY.includes("YOUR_")) return true
try {
const res = await fetch(
`https://phonevalidation.abstractapi.com/v1/?api_key=${ABSTRACT_API_KEY}&phone=${encodeURIComponent(phone)}`
)
const json = await res.json()
return json.valid === true
} catch {
return true // fail open on any network error
}
}
⚠️ CRITICAL: The if (!ABSTRACT_API_KEY || ABSTRACT_API_KEY.includes("YOUR_")) return true
guard MUST be the first line of checkPhoneReal. Without it, a placeholder key causes
the API to return valid: false for every real phone number, blocking all users.
Contact validator must be async, show "Checking..." during API call, fail open.
### Logic routing
Zero-indexed (A=0, B=1, C=2, D=3, E=4). Map user's logic exactly.
Disqualify routes call sendToSheet("disqualified") before goTo("disqualified").
Qualify routes call sendToSheet("qualified") before goTo("qualified").
### sendToSheet — CRITICAL (both files)
Always explicit key mapping. Never spread the answers object.
Every key must exactly match the Apps Script data.key.
function sendToSheet(outcome) {
if (!SHEET_URL || SHEET_URL.includes("YOUR_")) return
fetch(SHEET_URL, {
method: "POST",
mode: "no-cors",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
timestamp: new Date().toISOString(),
name: ans.name || "",
phone: ans.phone || "",
email: ans.email || "",
// one per question — key must match Apps Script exactly
outcome,
})
}).catch(() => {})
}
---
## Phase 5 — Output Sections
After both files are generated and presented, output these sections clearly.
### Step 1 — Google Sheet headers
Click cell A1 in your Google Sheet and paste a single tab-separated line.
Generate the correct columns for THIS form.
### Step 2 — Apps Script
1. Google Sheet → Extensions → Apps Script
2. Delete existing code, paste below, click Save
3. Deploy → New deployment → Web App
4. Execute as: Me | Access: Anyone → Deploy
5. Authorize, copy the Web App URL
6. Paste URL into both files where it says YOUR_APPS_SCRIPT_URL_HERE
function doPost(e) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const data = JSON.parse(e.postData.contents);
sheet.appendRow([
data.timestamp, // Column A — Timestamp
data.name, // Column B — Name
data.phone, // Column C — Phone
data.email, // Column D — Email
// one per field...
data.outcome // Column X — Outcome
]);
return ContentService.createTextOutput("OK");
}
### Step 3 — Phone validation key (only if form has phone field)
1. Go to: abstractapi.com/api/phone-validation
2. Sign up free (30 seconds, no credit card)
3. Copy API key and replace YOUR_ABSTRACT_API_KEY in both files.
Free tier: 250 checks/month. Without key, format validation still works.
### Step 4 — Embed instructions
Output platform-specific embed instructions for:
Framer, Webflow, Squarespace, Wix, WordPress, Custom HTML, React, Universal iFrame.
---
## Phase 6 — Common mistakes to avoid
| Mistake | Fix |
|---|---|
| height: 100vh on root | Use min-height: 100vh |
| overflow: hidden anywhere | Remove entirely |
| Spreading answers object in sendToSheet | Always explicit key map |
| position: absolute on active slides | position: relative when active |
| Missing autocomplete on HTML inputs | Add given-name, tel, email |
| OK button always enabled for choices | Disable until a choice is selected |
| Answer not saved before advancing | Save to answers object in validator |
| 1-indexed choice routing | Use 0-indexed (A=0, B=1, C=2...) |
| Apps Script keys not matching | Every data.X must match X: in sendToSheet |
| Sheet headers out of order | Column A = first item in appendRow |
| contact validator not async | Must be async when phone check included |
| HTML file has external dependencies | Must be fully self-contained |