
Signup Flow Cro
- 76 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
Signup Flow CRO is a Claude skill that optimizes signup and registration conversion through SSO strategy, field reduction, multi-step flow design, progressive profiling and A/B testing.
About
Signup Flow CRO is a conversion-rate-optimization framework for signup and registration flows. It covers authentication strategy and SSO selection, field-reduction methodology, multi-step flow architecture, credit-card-requirement analysis, post-submit experience, mobile signup patterns, progressive profiling and an A/B test framework. It ranks auth methods by friction with conversion-impact estimates and applies a 'Before First Use' test to decide which fields to keep, defer or drop. Growth teams use it to raise signup completion rates.
- Optimizes signup and registration flows: SSO strategy, field reduction, multi-step design and mobile patterns
- Ranks auth methods by friction with conversion-impact estimates (e.g. Google SSO +15-30%)
- Applies a 'Before First Use' test to cut or defer every non-essential field
Signup Flow Cro by the numbers
- 76 all-time installs (skills.sh)
- Ranked #498 of 853 Sales & Marketing skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
signup-flow-cro capabilities & compatibility
Free; a guidance and analysis framework, no API keys.
- Capabilities
- saas metrics coach
- Use cases
- ui design · marketing
- Pricing
- Free
What signup-flow-cro says it does
Signup and registration flow optimization covering SSO strategy, progressive profiling, field reduction, multi-step flow design, authentication UX, post-submit experience, and mobile registration
Current fields + completion rate + drop-off** — baseline and the friction point to cut (each removed field ~+10%)
For every field, ask: **Does the product literally not function without this data?**
npx skills add https://github.com/borghei/claude-skills --skill signup-flow-croAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 76 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
What it does
Optimize signup and registration flow conversion via SSO, field reduction and multi-step flow design.
Who is it for?
Growth and product teams raising signup completion rates on free-trial, freemium, paid or waitlist flows.
Skip if: Post-signup onboarding (use onboarding-cro) or lead-capture forms that are not account creation (use form-cro).
When should I use this skill?
You need to reduce signup friction, choose an SSO strategy, cut form fields, or A/B test a registration flow.
What you get
A lower-friction signup flow with the right SSO mix, a minimal field set, and an A/B test plan.
- Authentication and SSO strategy
- Reduced field set
- Multi-step flow design
By the numbers
- 8 auth methods ranked by friction
- each removed field estimated at ~+10% completion
- Google SSO estimated +15-30% vs email+password
Files
Signup Flow CRO
Production-grade signup and registration optimization framework covering authentication strategy, field reduction methodology, multi-step flow architecture, SSO implementation, progressive profiling, credit card requirement analysis, post-submit experience design, and mobile-specific registration patterns. For post-signup onboarding, use onboarding-cro. For lead capture forms (not account creation), use form-cro.
---
Table of Contents
- Initial Assessment
- Authentication Strategy
- Field Reduction Methodology
- Multi-Step Flow Architecture
- Credit Card Requirement Analysis
- Post-Submit Experience
- Mobile Signup Optimization
- Signup Flow Patterns by Product Type
- Progressive Profiling
- Error and Edge Case Handling
- A/B Test Framework
- Metrics and Benchmarks
- Output Artifacts
- Related Skills
---
Clarify First
Before optimizing the signup flow, confirm these inputs. If any is unknown or vague, ASK — do not assume:
- [ ] Flow type — free trial, freemium, paid, or waitlist (determines friction tolerance and minimum field set)
- [ ] B2B or B2C — B2B tolerates more fields; B2C needs minimal friction (sets SSO priority and field count)
- [ ] Current fields + completion rate + drop-off — baseline and the friction point to cut (each removed field ~+10%)
- [ ] Data truly needed before first product use — separates must-have fields from those to defer or enrich (the "Before First Use" test)
Stop rule: ask only the 2-3 that most change the output. If the user says "just draft it," proceed and list your assumptions at the top of the audit.
Initial Assessment
Required Context
| Question | Why It Matters |
|---|---|
| Flow type? (free trial, freemium, paid, waitlist) | Determines friction tolerance |
| B2B or B2C? | B2B tolerates more fields, B2C needs minimal friction |
| How many steps/screens currently? | Baseline for optimization |
| What fields are required? | Identifies reduction opportunities |
| Current completion rate? | Benchmark for improvement |
| Where do users drop off? (field-level data) | Pinpoints specific friction |
| What data is needed before first product use? | Separates must-have from nice-to-have |
| What compliance requirements exist? | Constrains what can be deferred |
---
Authentication Strategy
Authentication Methods Ranked by Friction
| Method | Friction Level | Best For | Conversion Impact |
|---|---|---|---|
| Google SSO (one-click) | Very low | B2B SaaS, productivity tools | +15-30% vs email+password |
| Apple Sign In | Very low | iOS/Mac-heavy audience | +10-20% on Apple devices |
| Microsoft SSO | Low | Enterprise B2B | +10-15% for enterprise |
| GitHub SSO | Low | Developer tools | +15-25% for dev audience |
| Magic link (email) | Low | Security-conscious, B2B | +5-10% vs password |
| Email + password | Medium | Universal fallback | Baseline |
| Phone + OTP | Medium | Mobile-first, B2C | Varies by market |
| Email + password + verification | High | When verification is required | -10-20% vs no verification |
SSO Strategy Decision
| Your Audience | Primary SSO | Secondary SSO | Keep Email+Password? |
|---|---|---|---|
| B2B SaaS (general) | Google Workspace | Microsoft | Yes |
| Developer tools | GitHub | Yes | |
| Enterprise | Microsoft/Okta | Yes (for personal evals) | |
| B2C consumer | Apple | Yes | |
| Mobile-first | Apple / Google | Phone OTP | Optional |
| Privacy-focused | Magic link | Email+password | Yes |
SSO Placement
┌──────────────────────────────────┐
│ Create your account │
│ │
│ [Continue with Google] │ ← SSO options first
│ [Continue with Microsoft] │
│ │
│ ──── or ──── │ ← Visual separator
│ │
│ Email: [_______________] │ ← Email+password as alternative
│ Password: [_______________] │
│ │
│ [Create Account] │
└──────────────────────────────────┘Rules:
- SSO buttons above the email form (not below)
- Use branded button styles (Google's official button, etc.)
- "or" divider between SSO and email options
- SSO reduces fields to zero (name and email come from the provider)
---
Field Reduction Methodology
The "Before First Use" Test
For every field, ask: Does the product literally not function without this data?
| Field | Passes Test? | Action |
|---|---|---|
| Yes (account identity) | Keep | |
| Password | Yes (account security) | Keep (or use SSO/magic link) |
| First name | Usually no | Defer to onboarding or profile |
| Last name | No | Defer or drop entirely |
| Company name | Usually no | Enrich from email domain |
| Phone number | Rarely | Defer unless SMS verification required |
| Job title | No | Defer to onboarding or enrich |
| Team size | No | Defer to onboarding |
| How did you hear about us? | Never | Post-signup survey or attribution |
| Industry | No | Enrich from company data |
Enrichment Sources
| Field | Enrichment Method | Timing |
|---|---|---|
| Company name | Email domain lookup (Clearbit, Apollo) | Immediately post-signup |
| Company size | Company data API | Immediately post-signup |
| Industry | Company data API | Immediately post-signup |
| Job title | LinkedIn API or manual CSM research | Before first sales contact |
| Location | IP geolocation | On signup |
Minimum Viable Field Sets
| Signup Type | Minimum Fields | Additional (if needed) |
|---|---|---|
| Freemium | Email only (or SSO) | -- |
| Free trial (product-led) | Email + Password (or SSO) | -- |
| Free trial (sales-assisted) | Email + Password + Company | + Role (for routing) |
| Paid signup | Email + Password + Payment | -- |
| Waitlist | + One qualifying question | |
| Enterprise trial | Email + Company + Role | + Team size (for provisioning) |
---
Multi-Step Flow Architecture
When to Use Multi-Step
| Condition | Single-Step | Multi-Step |
|---|---|---|
| Total fields | 1-4 | 5+ |
| Need to qualify/route | No | Yes |
| Product needs configuration | No | Yes |
| B2B with team setup | No | Yes |
Step Design
Step 1: Account Creation (lowest friction)
- Email + Password (or SSO)
- NOTHING else on this step
- This is where 60%+ of abandonment happens if overloaded
Step 2: Personalization (if needed)
- Role / goal / use case selection
- This personalizes their product experience
- Skip button available ("Set up later")
Step 3: Configuration (if needed)
- Team invite, integration connect, data import
- Each sub-step is optional with "Skip for now"
- Show value of completing each ("Invite your team to collaborate")
Progress Design
- Show step count: "Step 1 of 3"
- Show progress bar
- Label each step descriptively: "Create Account", "Your Role", "Your Team"
- Allow back navigation (preserve entered data)
- Never reset the form on back navigation or browser back button
---
Credit Card Requirement Analysis
Decision Framework
| Factor | Require CC | Do Not Require CC |
|---|---|---|
| Trial conversion goal | > 60% trial-to-paid | > 30% trial-to-paid with higher volume |
| Product complexity | Simple, immediate value | Complex, needs exploration |
| ACV | > $100/month | < $100/month |
| Sales motion | Product-led | Sales-assisted |
| Competitor practice | Competitors require CC | Competitors offer CC-free trial |
| Target audience | Enterprise (committed buyers) | SMB/prosumer (browsers) |
Impact Analysis
| Approach | Signup Volume | Trial Quality | Trial-to-Paid | Net Revenue |
|---|---|---|---|---|
| No CC required | Higher (+40-80%) | Lower (more tire-kickers) | Lower (2-15%) | Often higher net |
| CC required | Lower | Higher (committed) | Higher (40-70%) | Depends on volume |
| CC with "$0 charge" | Middle | Middle | Middle (20-40%) | Middle |
Recommendation Framework
Default to no CC required unless: 1. Your product delivers immediate, obvious value (no learning curve) 2. Your trial-to-paid with CC is > 50% 3. You have a high-touch sales team to handle lower volume 4. Support costs for free trials are unsustainable
If requiring CC: Display prominently:
- "You won't be charged until [date]"
- "Cancel anytime before [date]"
- "We'll email you 3 days before your trial ends"
---
Post-Submit Experience
Immediately After Signup
| Element | Implementation |
|---|---|
| Auto-login | Log the user in immediately (never force a separate login) |
| Welcome screen | Show a clear next step, not a blank dashboard |
| Confirmation email | Send immediately, include: what to expect, key features, support contact |
| Email verification | Defer if possible. If required, send inline and let them continue using the product before verifying |
Email Verification Strategy
| Approach | Impact on Activation | When to Use |
|---|---|---|
| No verification | Best activation rate | Low-risk products, freemium |
| Verify to unlock specific feature | Good -- users activate first | B2B SaaS with free tier |
| Verify within 24 hours | Moderate -- creates urgency | Products that send emails |
| Verify before any use | Worst activation rate | Regulated industries, financial products |
Default recommendation: Let users use the product immediately. Verify within 24-48 hours. Gate only the features that require a verified email (e.g., sending emails, team invites).
---
Mobile Signup Optimization
Mobile-Specific Rules
| Rule | Implementation |
|---|---|
| SSO first | Google/Apple Sign In is one tap on mobile |
| One column | Never use side-by-side fields on mobile |
| Large inputs | Minimum 44px height for all touch targets |
| Appropriate keyboards | type="email", type="tel", type="password" |
| Auto-fill support | Use standard field names for browser auto-fill |
| Sticky CTA | Pin "Create Account" button to bottom of viewport |
| No CAPTCHA | Use invisible reCAPTCHA or alternatives |
| Password visibility | Toggle to show/hide password |
Mobile vs Desktop Signup Differences
| Aspect | Desktop | Mobile |
|---|---|---|
| Primary auth | SSO or Email+Password | SSO preferred (one-tap) |
| Fields per screen | Up to 5 | Max 3 |
| Password rules | Show requirements upfront | Show on interaction |
| CAPTCHA | Standard reCAPTCHA acceptable | Invisible or none |
| Social proof | Sidebar or adjacent | Below form or above |
---
Signup Flow Patterns by Product Type
B2B SaaS Trial
[Google SSO] or [Email + Password]
→ Auto-login to product
→ Welcome screen: "What brings you here?" (3 options)
→ Guided first action based on selection
→ Team invite prompt (optional, day 2-3)B2C Consumer App
[Apple Sign In] or [Google Sign In] or [Email]
→ Immediately into product
→ Personalization (follows, preferences) inline
→ Profile completion deferredEnterprise/Sales-Assisted
[Work Email + Password + Company Name]
→ Auto-login to sandbox
→ Role + team size (for provisioning)
→ CSM outreach triggered for qualified accounts
→ Guided setup with dedicated supportWaitlist / Early Access
[Email only]
→ Confirmation page: position in waitlist
→ Referral mechanism: "Jump ahead by sharing"
→ Weekly update email on progress
→ Access granted email with one-click activation---
Progressive Profiling
Collect information over multiple sessions instead of one long form.
Progressive Profiling Schedule
| Session | What to Collect | How |
|---|---|---|
| Signup (session 1) | Email + auth | Signup form |
| First use (session 1-2) | Role, primary goal | In-product prompt or setup wizard |
| Day 3-5 | Team size, use case | Contextual question in product |
| Day 7-14 | Industry, company size | Survey or enrichment |
| Before first payment | Billing info | Upgrade flow |
Implementation Rules
- Each profiling touchpoint asks 1-2 questions maximum
- Always explain why you are asking ("So we can personalize your experience")
- Always provide a "Skip" option
- Never ask for information you can enrich automatically
- Store partial profiles and build over time
---
Error and Edge Case Handling
Password Requirements
| Approach | User Experience | Security |
|---|---|---|
| Show requirements upfront | Best -- user knows what to enter | Good |
| Show requirements on focus | Good | Good |
| Show errors only after submit | Bad -- frustrating | Same |
| Real-time checkmarks | Best -- progressive validation | Good |
Recommended: Show password requirements as a checklist that checks off in real-time as the user types.
Common Error Scenarios
| Error | Bad UX | Good UX |
|---|---|---|
| Email already registered | "Error: account exists" | "This email already has an account. [Log in] or [Reset password]" |
| Weak password | "Password too weak" | Checkmarks showing which requirements are met/unmet |
| SSO failure | Generic error page | "Something went wrong with Google login. [Try again] or [Use email instead]" |
| Network error | Form clears, no message | "Connection issue. Your data is saved. [Try again]" |
| Rate limiting | Blocked with no explanation | "Too many attempts. Please try again in [N] minutes" |
---
A/B Test Framework
High-Impact Tests
| Test | Hypothesis | Metric |
|---|---|---|
| Add Google SSO | SSO increases completion by 15-30% | Signup completion rate |
| Remove non-essential fields | Fewer fields = higher completion | Completion rate + activation rate |
| Single-step vs multi-step | Multi-step feels easier for 5+ field forms | Completion rate |
| CC required vs not | No CC increases volume enough to offset lower conversion | Net revenue |
| Defer email verification | Immediate product access increases activation | Activation rate |
Measurement Rules
- Track signup completion rate AND downstream activation rate
- A test that increases signups but decreases activation is not a win
- Track by traffic source (paid vs organic may respond differently)
- Track mobile and desktop separately
---
Metrics and Benchmarks
Key Metrics
| Metric | Formula | Benchmark |
|---|---|---|
| Signup page visit-to-completion | Completions / Page views | 30-50% (B2B), 40-60% (B2C) |
| SSO adoption rate | SSO signups / Total signups | 30-60% when offered |
| Field-level drop-off | Abandonment per field | Identify highest-drop field |
| Time to complete | Median seconds from first interaction to submit | < 45s for simple, < 2min for multi-step |
| Mobile completion rate | Mobile completions / Mobile page views | Should be within 15% of desktop |
| Email verification rate | Verified / Total signups | > 70% within 48 hours |
---
Output Artifacts
| Artifact | Format | Description |
|---|---|---|
| Signup Flow Audit | Issue/Impact/Fix/Priority table | Per-step analysis with estimated impact |
| Recommended Field Set | Justified list | Required vs deferrable fields with rationale |
| Authentication Strategy | Decision matrix | SSO options, placement, priority |
| Flow Redesign Spec | Step-by-step outline | Screen-by-screen design with copy |
| Progressive Profiling Plan | Session-by-session schedule | What to collect, when, and how |
| A/B Test Plan | Prioritized table | Top 5 tests with hypothesis and expected impact |
| Mobile Optimization Checklist | Per-element rules | Touch targets, keyboards, auto-fill, sticky CTA |
---
Tool Reference
1. signup_field_auditor.py
Audits a signup form configuration for unnecessary fields, missing enrichment opportunities, and friction points. Evaluates each field against the "Before First Use" test and recommends which to keep, defer, or enrich.
python scripts/signup_field_auditor.py fields.json --format text
python scripts/signup_field_auditor.py fields.json --format json| Flag | Type | Description |
|---|---|---|
fields.json | positional | Path to JSON file with form field configuration |
--format | optional | Output format: text (default) or json |
2. signup_flow_scorer.py
Scores a complete signup flow against conversion best practices. Evaluates SSO availability, field count, step count, mobile optimization, error handling, and post-submit experience. Outputs a 0-100 score with itemized improvements.
python scripts/signup_flow_scorer.py flow.json --format text
python scripts/signup_flow_scorer.py flow.json --format json| Flag | Type | Description |
|---|---|---|
flow.json | positional | Path to JSON file with signup flow configuration |
--format | optional | Output format: text (default) or json |
3. cc_requirement_analyzer.py
Analyzes whether to require a credit card for trial signup. Takes business metrics (ACV, trial-to-paid rate, support costs, competitors) and recommends CC-required, CC-free, or "$0 charge" approach with projected volume and revenue impact.
python scripts/cc_requirement_analyzer.py business.json --format text
python scripts/cc_requirement_analyzer.py business.json --format json| Flag | Type | Description |
|---|---|---|
business.json | positional | Path to JSON file with business metrics |
--format | optional | Output format: text (default) or json |
---
Troubleshooting
| Problem | Likely Cause | Resolution |
|---|---|---|
| Signup completion rate below 30% (B2B) or 40% (B2C) | Too many fields, no SSO option, or form on a separate page from the CTA | Reduce to email-only or SSO; keep form on the same page as the value proposition; each removed field improves conversion ~10% |
| SSO adoption rate below 30% when offered | SSO buttons placed below the email form, or wrong SSO providers for the audience | Move SSO buttons above the email form with "or" divider; match SSO to audience (Google for B2B, Apple for iOS users) |
| Mobile completion rate >15% below desktop | Form not optimized for touch (small inputs, wrong keyboard types, no auto-fill) | Ensure 44px min touch targets, use type="email"/type="tel", enable browser auto-fill, pin CTA to bottom of viewport |
| High drop-off on password field | Complex password requirements shown only after submission, or no password visibility toggle | Show requirements as real-time checklist, add show/hide toggle, consider magic link or SSO to eliminate password entirely |
| Email verification kills activation | Verification required before any product use blocks the critical first-session experience | Defer verification to 24-48 hours; allow product use immediately; gate only email-sending features behind verification |
| "Email already registered" errors are frequent | Users forget they have accounts; error message does not help them recover | Change error to "This email has an account. [Log in] or [Reset password]" with direct links |
| High abandonment on multi-step flows | Steps are not progressive, no progress indicator, or too many fields per step | Show step count and progress bar; limit step 1 to account creation only; add "Skip for now" on non-essential steps |
---
Success Criteria
- Signup page visit-to-completion rate reaches 30-50% (B2B) or 40-60% (B2C) within 60 days of optimization
- SSO adoption reaches 30-60% of total signups when SSO is properly offered
- Median time-to-complete stays below 45 seconds for simple flows and below 2 minutes for multi-step
- Mobile completion rate falls within 15% of desktop completion rate
- Email verification rate exceeds 70% within 48 hours of signup
- Field-level drop-off analysis shows no single field causing >10% incremental abandonment
- Post-signup activation rate (first key action) improves alongside signup rate (not a vanity metric tradeoff)
---
Scope & Limitations
In scope: Authentication strategy (SSO, magic link, email+password), field reduction methodology, multi-step flow architecture, credit card requirement analysis, post-submit experience design, mobile signup optimization, progressive profiling schedules, error and edge case handling, and A/B testing frameworks for registration flows.
Out of scope: Post-signup onboarding and activation (use onboarding-cro), non-registration forms like lead capture or contact forms (use form-cro), landing page conversion before the signup form (use page-cro), in-app upgrade and paywall flows (use paywall-upgrade-cro). Scripts operate on local data only -- no integrations with authentication providers (Auth0, Clerk, etc.) or analytics platforms.
Limitations: Conversion benchmarks are aggregate SaaS/app industry data and vary by vertical, price point, and audience. SSO adoption rates depend heavily on audience composition (developer audiences adopt GitHub SSO at 40%+, while SMB audiences may prefer email). Credit card requirement analysis is modeled on industry averages -- actual impact requires A/B testing in your specific context. Progressive profiling recommendations assume standard SaaS lifecycle stages.
---
Integration Points
- onboarding-cro -- Signup flow ends at account creation; onboarding-cro picks up from first login through activation
- form-cro -- Field-level optimization principles (validation, keyboard types, error handling) apply to signup forms
- page-cro -- Landing page quality directly impacts signup form reach; optimize the page before optimizing the form
- paywall-upgrade-cro -- Trial signup configuration (CC-required, trial length) affects downstream upgrade flow design
- pricing-strategy -- Pricing model (freemium vs trial) determines signup flow type and field requirements
- referral-program -- Referred user signups should pre-fill referrer context and display incentive
---
Related Skills
- onboarding-cro -- Use for post-signup activation optimization. Signup-flow-cro ends when the user has an account; onboarding-cro starts there.
- form-cro -- Use for non-signup forms (lead capture, contact, demo request). Different optimization framework than registration.
- page-cro -- Use when the landing page leading to signup is the bottleneck, not the signup form itself.
- paywall-upgrade-cro -- Use when the real challenge is converting free users to paid, not getting them to sign up.
#!/usr/bin/env python3
"""Credit Card Requirement Analyzer - Analyze whether to require CC for trial signup.
Takes business metrics and recommends CC-required, CC-free, or "$0 charge" approach
with projected volume and revenue impact modeling.
Usage:
python cc_requirement_analyzer.py business.json
python cc_requirement_analyzer.py business.json --format json
"""
import argparse
import json
import sys
from typing import Any
def safe_divide(num: float, den: float, default: float = 0.0) -> float:
"""Safely divide."""
return num / den if den != 0 else default
# Industry benchmark ranges
CC_BENCHMARKS = {
"cc_required": {
"signup_volume_multiplier": 1.0,
"trial_to_paid_range": (40, 70),
"lead_quality": "High (committed buyers)",
},
"cc_free": {
"signup_volume_multiplier": 1.6, # 40-80% more signups
"trial_to_paid_range": (2, 15),
"lead_quality": "Mixed (includes tire-kickers)",
},
"zero_charge": {
"signup_volume_multiplier": 1.3,
"trial_to_paid_range": (20, 40),
"lead_quality": "Medium (somewhat committed)",
},
}
def analyze_cc_requirement(data: dict) -> dict:
"""Analyze credit card requirement decision."""
metrics = data.get("metrics", {})
acv_monthly = metrics.get("acv_monthly", 0)
current_trial_signups = metrics.get("monthly_trial_signups", 0)
current_trial_to_paid = metrics.get("current_trial_to_paid_pct", 0)
product_complexity = metrics.get("product_complexity", "medium") # simple, medium, complex
sales_motion = metrics.get("sales_motion", "product_led") # product_led, sales_assisted
target_audience = metrics.get("target_audience", "smb") # smb, mid_market, enterprise
competitors_require_cc = metrics.get("competitors_require_cc", False)
support_cost_per_trial = metrics.get("support_cost_per_trial", 0)
avg_ltv = metrics.get("avg_customer_ltv", acv_monthly * 24)
# Scoring factors (positive = favor CC, negative = favor no CC)
factors = []
cc_score = 0 # Positive means CC-required is better
# ACV check
if acv_monthly >= 100:
factors.append({"factor": f"High ACV (${acv_monthly}/mo)", "direction": "CC-required", "weight": 2, "reason": "Higher price = more committed trialists expected"})
cc_score += 2
else:
factors.append({"factor": f"Low ACV (${acv_monthly}/mo)", "direction": "CC-free", "weight": 2, "reason": "Low-cost products benefit from volume; CC friction disproportionate to price"})
cc_score -= 2
# Complexity
if product_complexity == "simple":
factors.append({"factor": "Simple product", "direction": "CC-required", "weight": 1, "reason": "Quick time-to-value means users see ROI before trial ends"})
cc_score += 1
elif product_complexity == "complex":
factors.append({"factor": "Complex product", "direction": "CC-free", "weight": 2, "reason": "Users need time to explore before committing; CC gate prevents exploration"})
cc_score -= 2
else:
factors.append({"factor": "Medium complexity", "direction": "Neutral", "weight": 0, "reason": "Could go either way -- A/B test recommended"})
# Sales motion
if sales_motion == "sales_assisted":
factors.append({"factor": "Sales-assisted motion", "direction": "CC-required", "weight": 1, "reason": "Sales team can handle lower volume with higher-quality leads"})
cc_score += 1
else:
factors.append({"factor": "Product-led motion", "direction": "CC-free", "weight": 2, "reason": "PLG depends on volume and self-serve conversion"})
cc_score -= 2
# Audience
if target_audience == "enterprise":
factors.append({"factor": "Enterprise audience", "direction": "CC-required", "weight": 1, "reason": "Enterprise buyers are committed; CC friction is low relative to deal size"})
cc_score += 1
elif target_audience == "smb":
factors.append({"factor": "SMB audience", "direction": "CC-free", "weight": 2, "reason": "SMBs are browsers; CC requirement loses 40-80% of potential trialists"})
cc_score -= 2
# Competitors
if competitors_require_cc:
factors.append({"factor": "Competitors require CC", "direction": "CC-required", "weight": 1, "reason": "Market norm -- not requiring CC would be a differentiator but may signal low value"})
cc_score += 1
else:
factors.append({"factor": "Competitors offer CC-free trials", "direction": "CC-free", "weight": 2, "reason": "Not offering CC-free trial puts you at a competitive disadvantage"})
cc_score -= 2
# Support costs
if support_cost_per_trial > acv_monthly * 0.1:
factors.append({"factor": f"High support cost (${support_cost_per_trial}/trial)", "direction": "CC-required", "weight": 1, "reason": "CC requirement reduces trial volume and associated support burden"})
cc_score += 1
# Recommendation
if cc_score >= 3:
recommendation = "cc_required"
elif cc_score <= -3:
recommendation = "cc_free"
else:
recommendation = "zero_charge"
# Revenue projections for each approach
projections = {}
for approach, benchmarks in CC_BENCHMARKS.items():
volume_mult = benchmarks["signup_volume_multiplier"]
low_rate, high_rate = benchmarks["trial_to_paid_range"]
mid_rate = (low_rate + high_rate) / 2
projected_signups = int(current_trial_signups * volume_mult)
projected_customers_low = int(projected_signups * (low_rate / 100))
projected_customers_mid = int(projected_signups * (mid_rate / 100))
projected_customers_high = int(projected_signups * (high_rate / 100))
projections[approach] = {
"monthly_signups": projected_signups,
"trial_to_paid_range_pct": f"{low_rate}-{high_rate}%",
"monthly_customers_low": projected_customers_low,
"monthly_customers_mid": projected_customers_mid,
"monthly_customers_high": projected_customers_high,
"monthly_revenue_mid": round(projected_customers_mid * acv_monthly, 2),
"annual_revenue_mid": round(projected_customers_mid * acv_monthly * 12, 2),
"lead_quality": benchmarks["lead_quality"],
}
# Implementation guidance
if recommendation == "cc_required":
implementation = [
"Display prominently: 'You won't be charged until [trial end date]'",
"Add: 'Cancel anytime before [date]'",
"Send email reminder 3 days before trial ends",
"Offer easy one-click cancellation in account settings",
]
elif recommendation == "cc_free":
implementation = [
"Email-only signup (or SSO) with no payment information",
"Gate upgrade prompt to after activation event (aha moment)",
"Send value-based upgrade emails during trial",
"Use progressive profiling to collect business info for lead scoring",
]
else:
implementation = [
"Collect CC but display '$0.00 charge' confirmation",
"Show clear trial end date and 'cancel anytime' messaging",
"Send email reminder 3 days before trial ends",
"Consider A/B testing $0 charge vs CC-free to validate",
]
return {
"recommendation": recommendation,
"recommendation_label": recommendation.replace("_", " ").upper(),
"confidence": "High" if abs(cc_score) >= 4 else "Medium" if abs(cc_score) >= 2 else "Low -- A/B test recommended",
"cc_score": cc_score,
"factors": factors,
"projections": projections,
"implementation": implementation,
}
def format_text(result: dict) -> str:
"""Format analysis as human-readable text."""
lines = []
lines.append("=" * 60)
lines.append("CREDIT CARD REQUIREMENT ANALYSIS")
lines.append("=" * 60)
lines.append("")
lines.append(f"Recommendation: {result['recommendation_label']}")
lines.append(f"Confidence: {result['confidence']}")
lines.append("")
lines.append("-" * 60)
lines.append("DECISION FACTORS")
lines.append("-" * 60)
for f in result["factors"]:
direction = f["direction"]
lines.append(f"\n {f['factor']} -> {direction}")
lines.append(f" {f['reason']}")
lines.append("")
lines.append("-" * 60)
lines.append("REVENUE PROJECTIONS BY APPROACH")
lines.append("-" * 60)
for approach, proj in result["projections"].items():
label = approach.replace("_", " ").upper()
is_rec = " << RECOMMENDED" if approach == result["recommendation"] else ""
lines.append(f"\n {label}{is_rec}")
lines.append(f" Monthly Signups: {proj['monthly_signups']:>8,}")
lines.append(f" Trial-to-Paid: {proj['trial_to_paid_range_pct']:>8}")
lines.append(f" Customers/mo (mid): {proj['monthly_customers_mid']:>8,}")
lines.append(f" Monthly Revenue: ${proj['monthly_revenue_mid']:>10,.2f}")
lines.append(f" Annual Revenue: ${proj['annual_revenue_mid']:>10,.2f}")
lines.append(f" Lead Quality: {proj['lead_quality']}")
lines.append("")
lines.append("-" * 40)
lines.append("IMPLEMENTATION GUIDANCE")
lines.append("-" * 40)
for step in result["implementation"]:
lines.append(f" - {step}")
lines.append("")
return "\n".join(lines)
def main() -> None:
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Analyze whether to require credit card for trial signup."
)
parser.add_argument(
"input_file",
help="Path to JSON file with business metrics",
)
parser.add_argument(
"--format",
choices=["text", "json"],
default="text",
help="Output format (default: text)",
)
args = parser.parse_args()
try:
with open(args.input_file, "r") as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: File not found: {args.input_file}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON: {e}", file=sys.stderr)
sys.exit(1)
result = analyze_cc_requirement(data)
if args.format == "json":
print(json.dumps(result, indent=2))
else:
print(format_text(result))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Signup Field Auditor - Audit signup form fields for friction and reduction opportunities.
Evaluates each field against the "Before First Use" test, identifies enrichment
opportunities, and recommends which fields to keep, defer, or remove.
Usage:
python signup_field_auditor.py fields.json
python signup_field_auditor.py fields.json --format json
"""
import argparse
import json
import sys
from typing import Any
# Field classification database
FIELD_DATABASE = {
"email": {
"essential": True,
"reason": "Account identity -- required for authentication",
"recommendation": "keep",
},
"password": {
"essential": True,
"reason": "Account security (unless using SSO/magic link)",
"recommendation": "keep_or_replace",
"alternative": "Consider magic link or SSO to eliminate password entirely",
},
"first_name": {
"essential": False,
"reason": "Nice for personalization but not needed before first use",
"recommendation": "defer",
"defer_to": "onboarding or profile settings",
"enrichment": "Can be extracted from SSO provider",
},
"last_name": {
"essential": False,
"reason": "Rarely needed before first use",
"recommendation": "defer_or_remove",
"defer_to": "profile settings or enrich from SSO",
"enrichment": "Can be extracted from SSO provider",
},
"full_name": {
"essential": False,
"reason": "Personalization only -- defer or get from SSO",
"recommendation": "defer",
"defer_to": "onboarding or SSO auto-fill",
"enrichment": "SSO providers return full name",
},
"company": {
"essential": False,
"reason": "Can be enriched from email domain",
"recommendation": "enrich",
"enrichment": "Clearbit, Apollo, or email domain lookup",
},
"company_name": {
"essential": False,
"reason": "Can be enriched from email domain",
"recommendation": "enrich",
"enrichment": "Clearbit, Apollo, or email domain lookup",
},
"phone": {
"essential": False,
"reason": "Rarely needed at signup unless SMS verification required",
"recommendation": "defer_or_remove",
"defer_to": "sales qualification or profile",
},
"phone_number": {
"essential": False,
"reason": "Rarely needed at signup unless SMS verification required",
"recommendation": "defer_or_remove",
"defer_to": "sales qualification or profile",
},
"job_title": {
"essential": False,
"reason": "Used for routing/segmentation but not needed for product use",
"recommendation": "defer",
"defer_to": "onboarding (day 3-5) or enrich",
"enrichment": "LinkedIn API or CSM research",
},
"role": {
"essential": False,
"reason": "Used for personalization -- collect during onboarding, not signup",
"recommendation": "defer",
"defer_to": "onboarding welcome screen",
},
"team_size": {
"essential": False,
"reason": "Used for provisioning -- only needed for enterprise trials",
"recommendation": "defer",
"defer_to": "onboarding or enrich from company data",
"enrichment": "Company data API",
},
"industry": {
"essential": False,
"reason": "Used for segmentation -- can be enriched automatically",
"recommendation": "enrich",
"enrichment": "Company data API from email domain",
},
"how_did_you_hear": {
"essential": False,
"reason": "Attribution data -- never needed before first use",
"recommendation": "remove",
"alternative": "Use UTM parameters and attribution tracking instead",
},
"referral_source": {
"essential": False,
"reason": "Attribution data should come from tracking, not user input",
"recommendation": "remove",
"alternative": "UTM parameters, referrer header, attribution tools",
},
"address": {
"essential": False,
"reason": "Not needed unless shipping physical goods at signup",
"recommendation": "defer_or_remove",
"defer_to": "billing or shipping flow",
},
"country": {
"essential": False,
"reason": "Can be determined from IP geolocation",
"recommendation": "enrich",
"enrichment": "IP geolocation (on signup)",
},
"credit_card": {
"essential": False,
"reason": "Reduces signup volume 40-80%; only require if justified by business model",
"recommendation": "defer",
"defer_to": "upgrade flow or end of trial",
},
"agree_terms": {
"essential": True,
"reason": "Legal requirement for terms of service acceptance",
"recommendation": "keep",
},
"consent_marketing": {
"essential": True,
"reason": "GDPR requirement -- must be unchecked by default",
"recommendation": "keep",
},
}
def audit_field(field: dict) -> dict:
"""Audit a single field."""
name = field.get("name", "").lower().replace(" ", "_").replace("-", "_")
label = field.get("label", field.get("name", "Unknown"))
required = field.get("required", True)
# Look up in database
db_entry = FIELD_DATABASE.get(name)
if db_entry:
result = {
"field_name": label,
"field_key": name,
"required_in_form": required,
"essential_for_product": db_entry["essential"],
"recommendation": db_entry["recommendation"],
"reason": db_entry["reason"],
}
if "defer_to" in db_entry:
result["defer_to"] = db_entry["defer_to"]
if "enrichment" in db_entry:
result["enrichment_option"] = db_entry["enrichment"]
if "alternative" in db_entry:
result["alternative"] = db_entry["alternative"]
else:
# Unknown field -- flag for review
result = {
"field_name": label,
"field_key": name,
"required_in_form": required,
"essential_for_product": False,
"recommendation": "review",
"reason": f"Field '{name}' not in standard database -- manually assess if needed before first product use",
}
# Friction score (0 = no friction, 10 = maximum friction)
friction = 0
if name in ("email", "agree_terms", "consent_marketing"):
friction = 1
elif name == "password":
friction = 3
elif name in ("first_name", "full_name"):
friction = 2
elif name in ("last_name", "company", "company_name"):
friction = 3
elif name in ("phone", "phone_number"):
friction = 5
elif name in ("job_title", "role", "team_size", "industry"):
friction = 4
elif name in ("how_did_you_hear", "referral_source"):
friction = 4
elif name in ("credit_card",):
friction = 8
elif name in ("address",):
friction = 7
else:
friction = 4
result["friction_score"] = friction
return result
def audit_fields(data: dict) -> dict:
"""Audit all signup form fields."""
fields = data.get("fields", [])
signup_type = data.get("signup_type", "free_trial")
has_sso = data.get("has_sso", False)
field_results = []
total_friction = 0
keep_count = 0
defer_count = 0
remove_count = 0
enrich_count = 0
for field in fields:
result = audit_field(field)
field_results.append(result)
total_friction += result["friction_score"]
rec = result["recommendation"]
if rec in ("keep", "keep_or_replace"):
keep_count += 1
elif rec in ("defer", "defer_or_remove"):
defer_count += 1
elif rec == "remove":
remove_count += 1
elif rec == "enrich":
enrich_count += 1
# Minimum viable field set for signup type
min_fields = {
"freemium": ["email"],
"free_trial": ["email", "password"],
"free_trial_sales_assisted": ["email", "password", "company"],
"paid": ["email", "password", "credit_card"],
"waitlist": ["email"],
"enterprise_trial": ["email", "company", "role"],
}
recommended_min = min_fields.get(signup_type, ["email", "password"])
current_field_names = [f.get("name", "").lower().replace(" ", "_").replace("-", "_") for f in fields]
excess_fields = len(fields) - len(recommended_min)
# Overall assessment
if len(fields) <= len(recommended_min) + 1:
form_rating = "Excellent"
elif len(fields) <= len(recommended_min) + 3:
form_rating = "Good"
elif len(fields) <= 7:
form_rating = "Needs Reduction"
else:
form_rating = "High Friction"
# Estimated conversion impact
# Each unnecessary field reduces conversion by ~5-10%
unnecessary_count = defer_count + remove_count + enrich_count
estimated_conversion_lift = unnecessary_count * 7 # ~7% per removed field (conservative)
return {
"summary": {
"total_fields": len(fields),
"recommended_minimum": len(recommended_min),
"excess_fields": max(excess_fields, 0),
"form_rating": form_rating,
"total_friction_score": total_friction,
"keep": keep_count,
"defer": defer_count,
"remove": remove_count,
"enrich": enrich_count,
"has_sso": has_sso,
"signup_type": signup_type,
"estimated_conversion_lift_pct": estimated_conversion_lift,
},
"recommended_minimum_fields": recommended_min,
"field_audit": field_results,
"sso_note": "SSO eliminates email+password fields (name and email come from provider)" if not has_sso else "SSO available -- good",
}
def format_text(result: dict) -> str:
"""Format audit as human-readable text."""
lines = []
s = result["summary"]
lines.append("=" * 60)
lines.append("SIGNUP FIELD AUDIT")
lines.append("=" * 60)
lines.append("")
lines.append(f"Form Rating: {s['form_rating']}")
lines.append(f"Total Fields: {s['total_fields']} | Recommended Minimum: {s['recommended_minimum']} | Excess: {s['excess_fields']}")
lines.append(f"Friction Score: {s['total_friction_score']}")
lines.append(f"SSO Available: {'Yes' if s['has_sso'] else 'No -- adding SSO can boost signups 15-30%'}")
lines.append(f"Signup Type: {s['signup_type']}")
lines.append("")
lines.append(f"Actions: Keep {s['keep']} | Defer {s['defer']} | Remove {s['remove']} | Enrich {s['enrich']}")
if s["estimated_conversion_lift_pct"] > 0:
lines.append(f"Estimated Conversion Lift: ~{s['estimated_conversion_lift_pct']}% from removing unnecessary fields")
lines.append("")
lines.append(f"Minimum viable: {', '.join(result['recommended_minimum_fields'])}")
lines.append("")
lines.append("-" * 60)
lines.append("FIELD-BY-FIELD AUDIT")
lines.append("-" * 60)
for f in result["field_audit"]:
rec_label = f["recommendation"].upper().replace("_", " ")
lines.append(f"\n {f['field_name']} [{rec_label}] (friction: {f['friction_score']}/10)")
lines.append(f" Essential: {'Yes' if f['essential_for_product'] else 'No'}")
lines.append(f" Reason: {f['reason']}")
if "defer_to" in f:
lines.append(f" Defer to: {f['defer_to']}")
if "enrichment_option" in f:
lines.append(f" Enrichment: {f['enrichment_option']}")
if "alternative" in f:
lines.append(f" Alternative: {f['alternative']}")
lines.append("")
return "\n".join(lines)
def main() -> None:
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Audit signup form fields for friction and reduction opportunities."
)
parser.add_argument(
"input_file",
help="Path to JSON file with form field configuration",
)
parser.add_argument(
"--format",
choices=["text", "json"],
default="text",
help="Output format (default: text)",
)
args = parser.parse_args()
try:
with open(args.input_file, "r") as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: File not found: {args.input_file}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON: {e}", file=sys.stderr)
sys.exit(1)
result = audit_fields(data)
if args.format == "json":
print(json.dumps(result, indent=2))
else:
print(format_text(result))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Signup Flow Scorer - Score a signup flow against conversion best practices.
Evaluates SSO availability, field count, step count, mobile optimization, error
handling, and post-submit experience. Outputs 0-100 with itemized improvements.
Usage:
python signup_flow_scorer.py flow.json
python signup_flow_scorer.py flow.json --format json
"""
import argparse
import json
import sys
from typing import Any
SCORING_WEIGHTS = {
"authentication": 20,
"field_count": 20,
"flow_structure": 15,
"mobile_optimization": 15,
"error_handling": 10,
"post_submit": 10,
"security_ux": 10,
}
def score_authentication(data: dict) -> tuple[float, list[str]]:
"""Score authentication options (0-1)."""
score = 0.0
feedback = []
auth = data.get("authentication", {})
# SSO availability
sso_providers = auth.get("sso_providers", [])
if len(sso_providers) >= 2:
score += 0.4
elif len(sso_providers) == 1:
score += 0.25
feedback.append("Add a second SSO option (Google + Microsoft for B2B, Google + Apple for B2C)")
else:
feedback.append("No SSO -- adding Google/Apple SSO can increase completion by 15-30%")
# SSO placement
if auth.get("sso_above_email_form", False):
score += 0.2
elif sso_providers:
feedback.append("Move SSO buttons above the email form (not below) with 'or' divider")
# Magic link option
if auth.get("has_magic_link", False):
score += 0.1
# Password-less option available
if sso_providers or auth.get("has_magic_link", False):
score += 0.15
else:
feedback.append("No password-less option -- consider magic link for lower friction")
# Branded SSO buttons
if auth.get("branded_sso_buttons", True) and sso_providers:
score += 0.15
elif sso_providers:
feedback.append("Use official branded button styles for SSO (Google, Apple, Microsoft guidelines)")
return min(score, 1.0), feedback
def score_field_count(data: dict) -> tuple[float, list[str]]:
"""Score field count (0-1)."""
score = 0.0
feedback = []
fields = data.get("fields", [])
field_count = len(fields)
if field_count <= 2:
score = 1.0
elif field_count <= 4:
score = 0.7
feedback.append(f"{field_count} fields -- consider reducing to email + password (or SSO only)")
elif field_count <= 6:
score = 0.4
feedback.append(f"{field_count} fields is above optimal -- each removed field improves conversion ~7-10%")
elif field_count <= 8:
score = 0.2
feedback.append(f"{field_count} fields creates significant friction -- an 11-field form converts 120% worse than 4 fields")
else:
feedback.append(f"{field_count} fields is critically high -- audit each field with 'Before First Use' test")
# Check for known high-friction fields
field_names = [f.get("name", "").lower() for f in fields]
if "phone" in field_names or "phone_number" in field_names:
score -= 0.1
feedback.append("Phone number field adds significant friction -- defer unless SMS verification required")
if "credit_card" in field_names or "payment" in field_names:
score -= 0.1
feedback.append("Credit card at signup reduces volume 40-80% -- ensure this is justified")
return max(min(score, 1.0), 0.0), feedback
def score_flow_structure(data: dict) -> tuple[float, list[str]]:
"""Score flow structure (0-1)."""
score = 0.0
feedback = []
flow = data.get("flow_structure", {})
steps = flow.get("step_count", 1)
fields_total = len(data.get("fields", []))
# Step count appropriateness
if fields_total <= 4 and steps == 1:
score += 0.4
elif fields_total > 4 and 2 <= steps <= 3:
score += 0.4
elif steps > 3:
score += 0.15
feedback.append(f"{steps} steps is too many -- condense to 2-3 maximum")
elif fields_total > 4 and steps == 1:
score += 0.2
feedback.append("5+ fields on a single step -- consider multi-step to reduce perceived friction")
# Progress indicator
if steps > 1:
if flow.get("has_progress_indicator", False):
score += 0.2
else:
feedback.append("Multi-step form needs a progress indicator ('Step 1 of 3')")
# Back navigation
if steps > 1:
if flow.get("preserves_data_on_back", False):
score += 0.1
else:
feedback.append("Back navigation must preserve entered data (never reset form)")
# Skip options
if flow.get("non_essential_steps_skippable", False):
score += 0.15
elif steps > 1:
feedback.append("Add 'Skip for now' on non-essential steps (personalization, team setup)")
# Account creation is step 1
if flow.get("account_creation_first_step", True):
score += 0.15
else:
feedback.append("Account creation (email/password) should always be Step 1 -- 60%+ of abandonment happens here if overloaded")
return min(score, 1.0), feedback
def score_mobile(data: dict) -> tuple[float, list[str]]:
"""Score mobile optimization (0-1)."""
score = 0.0
feedback = []
mobile = data.get("mobile", {})
if mobile.get("single_column_layout", True):
score += 0.2
else:
feedback.append("Mobile must use single-column layout -- never side-by-side fields")
if mobile.get("min_touch_target_44px", False):
score += 0.2
else:
feedback.append("All touch targets must be minimum 44px height")
if mobile.get("appropriate_keyboard_types", False):
score += 0.2
else:
feedback.append("Use type='email', type='tel', type='password' for appropriate mobile keyboards")
if mobile.get("autofill_support", False):
score += 0.15
else:
feedback.append("Enable browser auto-fill with standard field names (autocomplete attribute)")
if mobile.get("sticky_cta", False):
score += 0.15
else:
feedback.append("Pin 'Create Account' button to bottom of viewport on mobile")
if mobile.get("no_captcha_or_invisible", True):
score += 0.1
else:
feedback.append("Use invisible reCAPTCHA on mobile -- visual CAPTCHA kills mobile conversion")
return min(score, 1.0), feedback
def score_error_handling(data: dict) -> tuple[float, list[str]]:
"""Score error handling (0-1)."""
score = 0.0
feedback = []
errors = data.get("error_handling", {})
if errors.get("inline_validation", False):
score += 0.3
else:
feedback.append("Add inline validation (real-time feedback as user types, not just on submit)")
if errors.get("password_checklist_realtime", False):
score += 0.2
else:
feedback.append("Show password requirements as a real-time checklist that checks off as user types")
if errors.get("existing_email_helpful_message", False):
score += 0.2
else:
feedback.append("'Email already registered' error should include [Log in] and [Reset password] links")
if errors.get("network_error_preserves_data", False):
score += 0.15
else:
feedback.append("Network errors must preserve form data with a 'Try again' button")
if errors.get("rate_limiting_message", False):
score += 0.15
else:
feedback.append("Rate limiting should show clear message with wait time, not a generic error")
return min(score, 1.0), feedback
def score_post_submit(data: dict) -> tuple[float, list[str]]:
"""Score post-submit experience (0-1)."""
score = 0.0
feedback = []
post = data.get("post_submit", {})
if post.get("auto_login", False):
score += 0.35
else:
feedback.append("Auto-login after signup is critical -- never force a separate login")
if post.get("welcome_screen", False):
score += 0.2
else:
feedback.append("Show a welcome screen with clear next step, not a blank dashboard")
if post.get("immediate_confirmation_email", False):
score += 0.15
verify = post.get("email_verification_strategy", "")
if verify in ("deferred", "gate_specific_features"):
score += 0.3
elif verify == "immediate_required":
feedback.append("Requiring email verification before any use kills activation -- defer to 24-48 hours")
else:
score += 0.15
return min(score, 1.0), feedback
def score_security_ux(data: dict) -> tuple[float, list[str]]:
"""Score security UX (0-1)."""
score = 0.0
feedback = []
security = data.get("security_ux", {})
if security.get("password_show_hide_toggle", False):
score += 0.3
else:
feedback.append("Add show/hide password toggle")
if security.get("password_strength_indicator", False):
score += 0.3
else:
feedback.append("Add password strength indicator")
if security.get("sso_failure_fallback", False):
score += 0.2
else:
feedback.append("SSO failure should show 'Try again' + 'Use email instead' options, not a generic error")
if security.get("https_visible", True):
score += 0.2
return min(score, 1.0), feedback
def score_flow(data: dict) -> dict:
"""Score complete signup flow."""
scorers = {
"authentication": score_authentication,
"field_count": score_field_count,
"flow_structure": score_flow_structure,
"mobile_optimization": score_mobile,
"error_handling": score_error_handling,
"post_submit": score_post_submit,
"security_ux": score_security_ux,
}
total = 0.0
components = {}
for key, scorer_fn in scorers.items():
raw_score, feedback = scorer_fn(data)
weight = SCORING_WEIGHTS[key]
weighted = raw_score * weight
total += weighted
components[key] = {
"raw_score_pct": round(raw_score * 100, 1),
"weight": weight,
"weighted_score": round(weighted, 1),
"feedback": feedback,
}
rating = "Excellent" if total >= 80 else "Good" if total >= 60 else "Needs Improvement" if total >= 40 else "Poor"
return {
"total_score": round(total, 1),
"rating": rating,
"max_possible": 100,
"components": components,
}
def format_text(result: dict) -> str:
"""Format score as human-readable text."""
lines = []
lines.append("=" * 60)
lines.append("SIGNUP FLOW SCORE")
lines.append("=" * 60)
lines.append("")
lines.append(f"Total Score: {result['total_score']}/100 ({result['rating']})")
lines.append("")
for key, comp in result["components"].items():
label = key.replace("_", " ").title()
lines.append(f"{label}: {comp['raw_score_pct']}% (weight: {comp['weight']}pts -> {comp['weighted_score']}pts)")
for fb in comp["feedback"]:
lines.append(f" - {fb}")
lines.append("")
return "\n".join(lines)
def main() -> None:
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Score signup flow against conversion best practices."
)
parser.add_argument(
"input_file",
help="Path to JSON file with signup flow configuration",
)
parser.add_argument(
"--format",
choices=["text", "json"],
default="text",
help="Output format (default: text)",
)
args = parser.parse_args()
try:
with open(args.input_file, "r") as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: File not found: {args.input_file}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON: {e}", file=sys.stderr)
sys.exit(1)
result = score_flow(data)
if args.format == "json":
print(json.dumps(result, indent=2))
else:
print(format_text(result))
if __name__ == "__main__":
main()
Related skills
FAQ
How much does removing a field help?
The skill estimates roughly +10% completion per removed field, using a 'Before First Use' test to decide what to keep.
What auth method converts best?
One-click Google SSO is ranked very-low friction with about +15-30% versus email plus password.