You are an expert in form optimization. Your goal is to maximize form completion rates while capturing the data that matters.
Initial Assessment
Check for product marketing context first:
If .claude/product-marketing-context.md exists, read it before asking questions. Use that context and only ask for information not already covered or specific to this task.
Before providing recommendations, identify:
-
Form Type
- Lead capture (gated content, newsletter)
- Contact form
- Demo/sales request
- Application form
- Survey/feedback
- Checkout form
- Quote request
-
Current State
- How many fields?
- What’s the current completion rate?
- Mobile vs. desktop split?
- Where do users abandon?
-
Business Context
- What happens with form submissions?
- Which fields are actually used in follow-up?
- Are there compliance/legal requirements?
Core Principles
→ See references/form-cro-playbook.md for details
For each issue:
- Issue: What’s wrong
- Impact: Estimated effect on conversions
- Fix: Specific recommendation
- Priority: High/Medium/Low
- Required fields: Justified list
- Optional fields: With rationale
- Field order: Recommended sequence
- Copy: Labels, placeholders, button
- Error messages: For each field
- Layout: Visual guidance
Test Hypotheses
Ideas to A/B test with expected outcomes
Experiment Ideas
Layout & Flow
- Single-step form vs. multi-step with progress bar
- 1-column vs. 2-column field layout
- Form embedded on page vs. separate page
- Vertical vs. horizontal field alignment
- Form above fold vs. after content
Field Optimization
- Reduce to minimum viable fields
- Add or remove phone number field
- Add or remove company/organization field
- Test required vs. optional field balance
- Use field enrichment to auto-fill known data
- Hide fields for returning/known visitors
Smart Forms
- Add real-time validation for emails and phone numbers
- Progressive profiling (ask more over time)
- Conditional fields based on earlier answers
- Auto-suggest for company names
Copy & Design Experiments
Labels & Microcopy
- Test field label clarity and length
- Placeholder text optimization
- Help text: show vs. hide vs. on-hover
- Error message tone (friendly vs. direct)
CTAs & Buttons
- Button text variations (“Submit” vs. “Get My Quote” vs. specific action)
- Button color and size testing
- Button placement relative to fields
Trust Elements
- Add privacy assurance near form
- Show trust badges next to submit
- Add testimonial near form
- Display expected response time
Demo Request Forms
- Test with/without phone number requirement
- Add “preferred contact method” choice
- Include “What’s your biggest challenge?” question
- Test calendar embed vs. form submission
Lead Capture Forms
- Email-only vs. email + name
- Test value proposition messaging above form
- Gated vs. ungated content strategies
- Post-submission enrichment questions
Contact Forms
- Add department/topic routing dropdown
- Test with/without message field requirement
- Show alternative contact methods (chat, phone)
- Expected response time messaging
Mobile & UX Experiments
- Larger touch targets for mobile
- Test appropriate keyboard types by field
- Sticky submit button on mobile
- Auto-focus first field on page load
- Test form container styling (card vs. minimal)
Task-Specific Questions
- What’s your current form completion rate?
- Do you have field-level analytics?
- What happens with the data after submission?
- Which fields are actually used in follow-up?
- Are there compliance/legal requirements?
- What’s the mobile vs. desktop split?
- signup-flow-cro — WHEN: the form being optimized is an account creation or trial registration form specifically. WHEN NOT: don’t use signup-flow-cro for lead capture, contact, or demo request forms; form-cro is the right tool.
- popup-cro — WHEN: the form lives inside a modal, exit-intent popup, or slide-in widget rather than embedded on a page. WHEN NOT: don’t use popup-cro for standalone page-embedded forms.
- page-cro — WHEN: the page containing the form is itself underperforming — poor value prop, weak headline, or mismatched traffic source. Fix the page context before or alongside the form. WHEN NOT: don’t invoke page-cro if the form is the only conversion element on a dedicated landing page and the page itself is fine.
- ab-test-setup — WHEN: specific form hypotheses are ready to test (field count, button copy, multi-step vs. single-step). WHEN NOT: don’t use ab-test-setup before the audit identifies the most impactful change to test.
- analytics-tracking — WHEN: field-level drop-off data doesn’t exist yet and the team needs to instrument form analytics before any optimization can happen. WHEN NOT: skip if analytics are already in place.
- marketing-context — WHEN: check
.claude/product-marketing-context.md for ICP and qualification criteria, which directly informs which fields are truly necessary. WHEN NOT: skip if user has explicitly listed the fields and their business rationale.
Communication
All form CRO output follows this quality standard:
- Every field recommendation is justified — never just “remove fields” without explaining which and why
- Audit output uses the Issue / Impact / Fix / Priority structure consistently
- Multi-step vs. single-step recommendation always includes the qualifying criteria for the choice
- Mobile optimization is addressed separately from desktop — never conflate the two
- Submit button copy alternatives are always provided (minimum 3 options with reasoning)
- Error message rewrites are included when error handling is flagged as an issue
Proactive Triggers
Automatically surface form-cro when:
- “Our lead form isn’t converting” — Any complaint about form completion rates immediately triggers the field audit and core principles review.
- Demo request or contact page being built — When frontend-design or copywriting skills are active and a form is part of the page, proactively offer form-cro review.
- “We’re getting leads but bad quality” — Poor lead quality often signals wrong fields or missing qualification questions; proactively recommend field audit.
- Mobile conversion gap detected — If page-cro or analytics review shows a desktop vs. mobile completion gap on a form, surface form-cro mobile optimization checklist.
- Long form identified — When user describes or shares a form with 7+ fields, immediately flag the field-cost framework and multi-step recommendation.
Output Artifacts
| Artifact | Format | Description |
|---|
| Form Audit | Issue/Impact/Fix/Priority table | Per-field and per-pattern analysis with actionable fixes |
| Recommended Field Set | Justified list | Required vs. optional fields with rationale for each |
| Field Order & Layout Spec | Annotated outline | Recommended sequence, grouping, column layout, and mobile considerations |
| Submit Button Copy Options | 3-option table | Action-oriented button copy variants with reasoning |
| A/B Test Hypotheses | Table | Hypothesis × variant × success metric × priority for top 3-5 test ideas |
form-cro reference
Core Principles
1. Every Field Has a Cost
Each field reduces completion rate. Rule of thumb:
- 3 fields: Baseline
- 4-6 fields: 10-25% reduction
- 7+ fields: 25-50%+ reduction
For each field, ask:
- Is this absolutely necessary before we can help them?
- Can we get this information another way?
- Can we ask this later?
2. Value Must Exceed Effort
- Clear value proposition above form
- Make what they get obvious
- Reduce perceived effort (field count, labels)
3. Reduce Cognitive Load
- One question per field
- Clear, conversational labels
- Logical grouping and order
- Smart defaults where possible
Field-by-Field Optimization
Email Field
- Single field, no confirmation
- Inline validation
- Typo detection (did you mean gmail.com?)
- Proper mobile keyboard
Name Fields
- Single "Name" vs. First/Last — test this
- Single field reduces friction
- Split needed only if personalization requires it
Phone Number
- Make optional if possible
- If required, explain why
- Auto-format as they type
- Country code handling
Company/Organization
- Auto-suggest for faster entry
- Enrichment after submission (Clearbit, etc.)
- Consider inferring from email domain
Job Title/Role
- Dropdown if categories matter
- Free text if wide variation
- Consider making optional
Message/Comments (Free Text)
- Make optional
- Reasonable character guidance
- Expand on focus
Dropdown Selects
- "Select one..." placeholder
- Searchable if many options
- Consider radio buttons if < 5 options
- "Other" option with text field
Checkboxes (Multi-select)
- Clear, parallel labels
- Reasonable number of options
- Consider "Select all that apply" instruction
Form Layout Optimization
Field Order
- Start with easiest fields (name, email)
- Build commitment before asking more
- Sensitive fields last (phone, company size)
- Logical grouping if many fields
Labels and Placeholders
- Labels: Always visible (not just placeholder)
- Placeholders: Examples, not labels
- Help text: Only when genuinely helpful
Good:
Email
[[email protected]]
Bad:
[Enter your email address] ← Disappears on focus
Visual Design
- Sufficient spacing between fields
- Clear visual hierarchy
- CTA button stands out
- Mobile-friendly tap targets (44px+)
Single Column vs. Multi-Column
- Single column: Higher completion, mobile-friendly
- Multi-column: Only for short related fields (First/Last name)
- When in doubt, single column
Multi-Step Forms
When to Use Multi-Step
- More than 5-6 fields
- Logically distinct sections
- Conditional paths based on answers
- Complex forms (applications, quotes)
Multi-Step Best Practices
- Progress indicator (step X of Y)
- Start with easy, end with sensitive
- One topic per step
- Allow back navigation
- Save progress (don't lose data on refresh)
- Clear indication of required vs. optional
Progressive Commitment Pattern
- Low-friction start (just email)
- More detail (name, company)
- Qualifying questions
- Contact preferences
Error Handling
Inline Validation
- Validate as they move to next field
- Don't validate too aggressively while typing
- Clear visual indicators (green check, red border)
Error Messages
- Specific to the problem
- Suggest how to fix
- Positioned near the field
- Don't clear their input
Good: "Please enter a valid email address (e.g., [email protected])"
Bad: "Invalid input"
On Submit
- Focus on first error field
- Summarize errors if multiple
- Preserve all entered data
- Don't clear form on error
Submit Button Optimization
Button Copy
Weak: "Submit" | "Send"
Strong: "[Action] + [What they get]"
Examples:
- "Get My Free Quote"
- "Download the Guide"
- "Request Demo"
- "Send Message"
- "Start Free Trial"
Button Placement
- Immediately after last field
- Left-aligned with fields
- Sufficient size and contrast
- Mobile: Sticky or clearly visible
Post-Submit States
- Loading state (disable button, show spinner)
- Success confirmation (clear next steps)
- Error handling (clear message, focus on issue)
Trust and Friction Reduction
Near the Form
- Privacy statement: "We'll never share your info"
- Security badges if collecting sensitive data
- Testimonial or social proof
- Expected response time
Reducing Perceived Effort
- "Takes 30 seconds"
- Field count indicator
- Remove visual clutter
- Generous white space
Addressing Objections
- "No spam, unsubscribe anytime"
- "We won't share your number"
- "No credit card required"
Form Types: Specific Guidance
Lead Capture (Gated Content)
- Minimum viable fields (often just email)
- Clear value proposition for what they get
- Consider asking enrichment questions post-download
- Test email-only vs. email + name
Contact Form
- Essential: Email/Name + Message
- Phone optional
- Set response time expectations
- Offer alternatives (chat, phone)
Demo Request
- Name, Email, Company required
- Phone: Optional with "preferred contact" choice
- Use case/goal question helps personalize
- Calendar embed can increase show rate
Quote/Estimate Request
- Multi-step often works well
- Start with easy questions
- Technical details later
- Save progress for complex forms
Survey Forms
- Progress bar essential
- One question per screen for engagement
- Skip logic for relevance
- Consider incentive for completion
Mobile Optimization
- Larger touch targets (44px minimum height)
- Appropriate keyboard types (email, tel, number)
- Autofill support
- Single column only
- Sticky submit button
- Minimal typing (dropdowns, buttons)
Measurement
Key Metrics
- Form start rate: Page views → Started form
- Completion rate: Started → Submitted
- Field drop-off: Which fields lose people
- Error rate: By field
- Time to complete: Total and by field
- Mobile vs. desktop: Completion by device
What to Track
- Form views
- First field focus
- Each field completion
- Errors by field
- Submit attempts
- Successful submissions
#!/usr/bin/env python3
"""
Form Field Analyzer for CRO
Analyzes HTML forms for conversion optimization opportunities.
Checks field count, types, labels, friction signals, and mobile readiness.
Usage:
python3 form_field_analyzer.py # Demo mode
python3 form_field_analyzer.py form.html # Analyze HTML file
python3 form_field_analyzer.py form.html --json # JSON output
"""
import json
import sys
import os
import re
from html.parser import HTMLParser
class FormAnalyzer(HTMLParser):
def __init__(self):
super().__init__()
self.forms = []
self.current_form = None
self.in_label = False
self.current_label = ""
self.in_button = False
self.current_button = ""
def handle_starttag(self, tag, attrs):
attrs_dict = dict(attrs)
if tag == "form":
self.current_form = {
"action": attrs_dict.get("action", ""),
"method": attrs_dict.get("method", "GET").upper(),
"fields": [],
"buttons": [],
"has_autocomplete": "autocomplete" in attrs_dict
}
elif tag == "input" and self.current_form is not None:
input_type = attrs_dict.get("type", "text").lower()
if input_type not in ("hidden", "submit"):
self.current_form["fields"].append({
"type": input_type,
"name": attrs_dict.get("name", ""),
"placeholder": attrs_dict.get("placeholder", ""),
"required": "required" in attrs_dict,
"autocomplete": attrs_dict.get("autocomplete", ""),
"has_label": False
})
elif input_type == "submit":
self.current_form["buttons"].append(attrs_dict.get("value", "Submit"))
elif tag == "textarea" and self.current_form is not None:
self.current_form["fields"].append({
"type": "textarea",
"name": attrs_dict.get("name", ""),
"placeholder": attrs_dict.get("placeholder", ""),
"required": "required" in attrs_dict,
"autocomplete": "",
"has_label": False
})
elif tag == "select" and self.current_form is not None:
self.current_form["fields"].append({
"type": "select",
"name": attrs_dict.get("name", ""),
"placeholder": "",
"required": "required" in attrs_dict,
"autocomplete": "",
"has_label": False
})
elif tag == "label":
self.in_label = True
self.current_label = ""
for_attr = attrs_dict.get("for", "")
if for_attr and self.current_form:
for field in self.current_form["fields"]:
if field["name"] == for_attr:
field["has_label"] = True
elif tag == "button":
self.in_button = True
self.current_button = ""
def handle_data(self, data):
if self.in_label:
self.current_label += data.strip()
if self.in_button:
self.current_button += data.strip()
def handle_endtag(self, tag):
if tag == "form" and self.current_form:
self.forms.append(self.current_form)
self.current_form = None
elif tag == "label":
self.in_label = False
elif tag == "button":
self.in_button = False
if self.current_button and self.current_form:
self.current_form["buttons"].append(self.current_button)
def analyze_form(form):
"""Analyze a single form for CRO issues."""
fields = form["fields"]
issues = []
warnings = []
positives = []
field_count = len(fields)
# Field count analysis
if field_count > 7:
issues.append(f"Too many fields ({field_count}). Each field above 3 reduces conversion by ~5-10%. Consider progressive disclosure.")
elif field_count > 4:
warnings.append(f"{field_count} fields — acceptable but test reducing to 3-4 core fields.")
elif field_count <= 3:
positives.append(f"Low friction — only {field_count} fields.")
# Phone number field
phone_fields = [f for f in fields if "phone" in f["name"].lower() or f["type"] == "tel"]
if phone_fields:
required_phones = [f for f in phone_fields if f["required"]]
if required_phones:
issues.append("Phone number is REQUIRED — this is the #1 form abandonment trigger. Make optional or remove.")
else:
warnings.append("Phone field present (optional) — still causes friction. Consider removing unless sales-critical.")
# Labels
unlabeled = [f for f in fields if not f["has_label"] and not f["placeholder"]]
if unlabeled:
issues.append(f"{len(unlabeled)} fields have no label AND no placeholder. Users won't know what to enter.")
placeholder_only = [f for f in fields if not f["has_label"] and f["placeholder"]]
if placeholder_only:
warnings.append(f"{len(placeholder_only)} fields use placeholder-only labels. Placeholders disappear on focus — use visible labels.")
# Button text
weak_ctas = ["submit", "send", "go", "ok"]
for btn in form["buttons"]:
if btn.lower() in weak_ctas:
warnings.append(f'CTA button says "{btn}" — use action-specific text like "Get My Free Report" or "Start Free Trial".')
if not form["buttons"]:
issues.append("No submit button found. Form may be broken or use JavaScript submission only.")
# Autocomplete
fields_with_autocomplete = [f for f in fields if f["autocomplete"]]
if not fields_with_autocomplete and field_count > 0:
warnings.append("No autocomplete attributes. Adding autocomplete reduces mobile friction significantly.")
# Required fields
required_count = sum(1 for f in fields if f["required"])
if required_count == field_count and field_count > 2:
warnings.append("ALL fields are required. Consider making some optional to reduce perceived commitment.")
# Score
score = 100
score -= len(issues) * 15
score -= len(warnings) * 5
score += len(positives) * 5
score = max(0, min(100, score))
return {
"field_count": field_count,
"required_count": required_count,
"has_phone": len(phone_fields) > 0,
"cta_text": form["buttons"],
"issues": issues,
"warnings": warnings,
"positives": positives,
"score": score,
"fields": [{"name": f["name"], "type": f["type"], "required": f["required"]} for f in fields]
}
def format_report(analyses):
"""Format human-readable report."""
lines = []
lines.append("")
lines.append("=" * 60)
lines.append(" FORM CRO — FIELD ANALYSIS REPORT")
lines.append("=" * 60)
for i, analysis in enumerate(analyses):
lines.append("")
lines.append(f" FORM {i + 1}")
lines.append(f" Fields: {analysis['field_count']} | Required: {analysis['required_count']} | CTA: {', '.join(analysis['cta_text']) or 'none'}")
lines.append("")
score = analysis["score"]
bar = "█" * (score // 5) + "░" * (20 - score // 5)
lines.append(f" FORM SCORE: {score}/100")
lines.append(f" [{bar}]")
lines.append("")
lines.append(" Fields:")
for f in analysis["fields"]:
req = " *" if f["required"] else ""
lines.append(f" [{f['type']}] {f['name']}{req}")
lines.append("")
if analysis["positives"]:
lines.append(" 🟢 STRENGTHS:")
for p in analysis["positives"]:
lines.append(f" ✓ {p}")
lines.append("")
if analysis["issues"]:
lines.append(" 🔴 ISSUES:")
for issue in analysis["issues"]:
lines.append(f" • {issue}")
lines.append("")
if analysis["warnings"]:
lines.append(" 🟡 WARNINGS:")
for warn in analysis["warnings"]:
lines.append(f" • {warn}")
lines.append("")
return "\n".join(lines)
SAMPLE_HTML = """
<form action="/submit" method="POST">
<label for="name">Full Name</label>
<input type="text" name="name" id="name" required placeholder="John Smith">
<label for="email">Work Email</label>
<input type="email" name="email" id="email" required placeholder="[email protected]">
<label for="company">Company</label>
<input type="text" name="company" id="company" required>
<label for="phone">Phone Number</label>
<input type="tel" name="phone" id="phone" required>
<label for="role">Job Title</label>
<input type="text" name="role" id="role" required>
<label for="employees">Company Size</label>
<select name="employees" id="employees" required>
<option value="">Select...</option>
<option value="1-10">1-10</option>
<option value="11-50">11-50</option>
<option value="51-200">51-200</option>
<option value="200+">200+</option>
</select>
<label for="message">How can we help?</label>
<textarea name="message" id="message" placeholder="Tell us about your needs..."></textarea>
<button type="submit">Submit</button>
</form>
"""
def main():
use_json = "--json" in sys.argv
args = [a for a in sys.argv[1:] if a != "--json"]
if args and os.path.isfile(args[0]):
with open(args[0]) as f:
html = f.read()
else:
if not args:
print("[Demo mode — analyzing sample lead capture form]")
html = SAMPLE_HTML
parser = FormAnalyzer()
parser.feed(html)
if not parser.forms:
print("No <form> elements found in the HTML.")
sys.exit(1)
analyses = [analyze_form(form) for form in parser.forms]
if use_json:
print(json.dumps(analyses, indent=2))
else:
print(format_report(analyses))
if __name__ == "__main__":
main()