feat: add landing-page-generator skill for high-converting web pages (#341)
* feat: add landing-page-generator skill for high-converting web pages * fix: restore missing sections and fix corruption in SKILL.md
This commit is contained in:
203
skills/landing-page-generator/SKILL.md
Normal file
203
skills/landing-page-generator/SKILL.md
Normal file
@@ -0,0 +1,203 @@
|
||||
---
|
||||
name: "landing-page-generator"
|
||||
description: "Generates high-converting Next.js/React landing pages with Tailwind CSS. Uses PAS, AIDA, and BAB frameworks for optimized copy/components (Heroes, Features, Pricing). Focuses on Core Web Vitals/SEO."
|
||||
category: "front-end"
|
||||
risk: "safe"
|
||||
source: "community"
|
||||
date_added: "2026-03-18"
|
||||
author: "alirezarezvani"
|
||||
tags: ["nextjs", "react", "tailwind", "landing-page", "marketing", "seo", "cro"]
|
||||
tools: ["claude", "cursor", "gemini"]
|
||||
---
|
||||
|
||||
# Landing Page Generator
|
||||
|
||||
Generate high-converting landing pages from a product description. Output complete Next.js/React components with multiple section variants, proven copy frameworks, SEO optimization, and performance-first patterns. Not lorem ipsum — actual copy that converts.
|
||||
|
||||
**Target:** LCP < 1s · CLS < 0.1 · FID < 100ms
|
||||
**Output:** TSX components + Tailwind styles + SEO meta + copy variants
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
- 5 hero section variants (centered, split, gradient, video-bg, minimal)
|
||||
- Feature sections (grid, alternating, cards with icons)
|
||||
- Pricing tables (2–4 tiers with feature lists and toggle)
|
||||
- FAQ accordion with schema markup
|
||||
- Testimonials (grid, carousel, single-quote)
|
||||
- CTA sections (banner, full-page, inline)
|
||||
- Footer (simple, mega, minimal)
|
||||
- 4 design styles with Tailwind class sets
|
||||
|
||||
---
|
||||
|
||||
## Generation Workflow
|
||||
|
||||
Follow these steps in order for every landing page request:
|
||||
|
||||
1. **Gather inputs** — collect product name, tagline, audience, pain point, key benefit, pricing tiers, design style, and copy framework using the trigger format below. Ask only for missing fields.
|
||||
2. **Analyze brand voice** (recommended) — if the user has existing brand content (website copy, blog posts, marketing materials), run it through `marketing-skill/content-production/scripts/brand_voice_analyzer.py` to get a voice profile (formality, tone, perspective). Use the profile to inform design style and copy framework selection:
|
||||
- formal + professional → **enterprise** style, **AIDA** framework
|
||||
- casual + friendly → **bold-startup** style, **BAB** framework
|
||||
- professional + authoritative → **dark-saas** style, **PAS** framework
|
||||
- casual + conversational → **clean-minimal** style, **BAB** framework
|
||||
3. **Select design style** — map the user's choice (or infer from brand voice analysis) to one of the four Tailwind class sets in the Design Style Reference.
|
||||
4. **Apply copy framework** — write all headline and body copy using the chosen framework (PAS / AIDA / BAB) before generating components. Match the voice profile's formality and tone throughout.
|
||||
5. **Generate sections in order** — Hero → Features → Pricing → FAQ → Testimonials → CTA → Footer. Skip sections not relevant to the product.
|
||||
6. **Validate against SEO checklist** — run through every item in the SEO Checklist before outputting final code. Fix any gaps inline.
|
||||
7. **Output final components** — deliver complete, copy-paste-ready TSX files with all Tailwind classes, SEO meta, and structured data included.
|
||||
|
||||
---
|
||||
|
||||
## Triggering This Skill
|
||||
|
||||
```
|
||||
Product: [name]
|
||||
Tagline: [one sentence value prop]
|
||||
Target audience: [who they are]
|
||||
Key pain point: [what problem you solve]
|
||||
Key benefit: [primary outcome]
|
||||
Pricing tiers: [free/pro/enterprise or describe]
|
||||
Design style: dark-saas | clean-minimal | bold-startup | enterprise
|
||||
Copy framework: PAS | AIDA | BAB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Design Style Reference
|
||||
|
||||
| Style | Background | Accent | Cards | CTA Button |
|
||||
|---|---|---|---|---|
|
||||
| **Dark SaaS** | `bg-gray-950 text-white` | `violet-500/400` | `bg-gray-900 border border-gray-800` | `bg-violet-600 hover:bg-violet-500` |
|
||||
| **Clean Minimal** | `bg-white text-gray-900` | `blue-600` | `bg-gray-50 border border-gray-200 rounded-2xl` | `bg-blue-600 hover:bg-blue-700` |
|
||||
| **Bold Startup** | `bg-white text-gray-900` | `orange-500` | `shadow-xl rounded-3xl` | `bg-orange-500 hover:bg-orange-600 text-white` |
|
||||
| **Enterprise** | `bg-slate-50 text-slate-900` | `slate-700` | `bg-white border border-slate-200 shadow-sm` | `bg-slate-900 hover:bg-slate-800 text-white` |
|
||||
|
||||
> **Bold Startup** headings: add `font-black tracking-tight` to all `<h1>`/`<h2>` elements.
|
||||
|
||||
---
|
||||
|
||||
## Copy Frameworks
|
||||
|
||||
**PAS (Problem → Agitate → Solution)**
|
||||
- H1: Painful state they're in
|
||||
- Sub: What happens if they don't fix it
|
||||
- CTA: What you offer
|
||||
- *Example — H1:* "Your team wastes 3 hours a day on manual reporting" / *Sub:* "Every hour spent on spreadsheets is an hour not closing deals. Your competitors are already automated." / *CTA:* "Automate your reports in 10 minutes →"
|
||||
|
||||
**AIDA (Attention → Interest → Desire → Action)**
|
||||
- H1: Bold attention-grabbing statement → Sub: Interesting fact or benefit → Features: Desire-building proof points → CTA: Clear action
|
||||
|
||||
**BAB (Before → After → Bridge)**
|
||||
- H1: "[Before state] → [After state]" → Sub: "Here's how [product] bridges the gap" → Features: How it works (the bridge)
|
||||
|
||||
---
|
||||
|
||||
## Representative Component: Hero (Centered Gradient — Dark SaaS)
|
||||
|
||||
Use this as the structural template for all hero variants. Swap layout classes, gradient direction, and image placement for split, video-bg, and minimal variants.
|
||||
|
||||
```tsx
|
||||
export function HeroCentered() {
|
||||
return (
|
||||
<section className="relative flex min-h-screen flex-col items-center justify-center overflow-hidden bg-gray-950 px-4 text-center">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-violet-900/20 to-transparent" />
|
||||
<div className="pointer-events-none absolute -top-40 left-1/2 h-[600px] w-[600px] -translate-x-1/2 rounded-full bg-violet-600/20 blur-3xl" />
|
||||
<div className="relative z-10 max-w-4xl">
|
||||
<div className="mb-6 inline-flex items-center gap-2 rounded-full border border-violet-500/30 bg-violet-500/10 px-4 py-1.5 text-sm text-violet-300">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-violet-400" />
|
||||
Now in public beta
|
||||
</div>
|
||||
<h1 className="mb-6 text-5xl font-bold tracking-tight text-white md:text-7xl">
|
||||
Ship faster.<br />
|
||||
<span className="bg-gradient-to-r from-violet-400 to-pink-400 bg-clip-text text-transparent">
|
||||
Break less.
|
||||
</span>
|
||||
</h1>
|
||||
<p className="mx-auto mb-10 max-w-2xl text-xl text-gray-400">
|
||||
The deployment platform that catches errors before your users do.
|
||||
Zero config. Instant rollbacks. Real-time monitoring.
|
||||
</p>
|
||||
<div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-center">
|
||||
<Button size="lg" className="bg-violet-600 text-white hover:bg-violet-500 px-8">
|
||||
Start free trial
|
||||
</Button>
|
||||
<Button size="lg" variant="outline" className="border-gray-700 text-gray-300">
|
||||
See how it works →
|
||||
</Button>
|
||||
</div>
|
||||
<p className="mt-4 text-sm text-gray-500">No credit card required · 14-day free trial</p>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Other Section Patterns
|
||||
|
||||
### Feature Section (Alternating)
|
||||
|
||||
Map over a `features` array with `{ title, description, image, badge }`. Toggle layout direction with `i % 2 === 1 ? "lg:flex-row-reverse" : ""`. Use `<Image>` with explicit `width`/`height` and `rounded-2xl shadow-xl`. Wrap in `<section className="py-24">` with `max-w-6xl` container.
|
||||
|
||||
### Pricing Table
|
||||
|
||||
Map over a `plans` array with `{ name, price, description, features[], cta, highlighted }`. Highlighted plan gets `border-2 border-violet-500 bg-violet-950/50 ring-4 ring-violet-500/20`; others get `border border-gray-800 bg-gray-900`. Render `null` price as "Custom". Use `<Check>` icon per feature row. Layout: `grid gap-8 lg:grid-cols-3`.
|
||||
|
||||
### FAQ with Schema Markup
|
||||
|
||||
Inject `FAQPage` JSON-LD via `<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }} />` inside the section. Map FAQs with `{ q, a }` into shadcn `<Accordion>` with `type="single" collapsible`. Container: `max-w-3xl`.
|
||||
|
||||
### Testimonials, CTA, Footer
|
||||
|
||||
- **Testimonials:** Grid (`grid-cols-1 md:grid-cols-3`) or single-quote hero block with avatar, name, role, and quote text.
|
||||
- **CTA Banner:** Full-width section with headline, subhead, and two buttons (primary + ghost). Add trust signals (money-back guarantee, logo strip) immediately below.
|
||||
- **Footer:** Logo + nav columns + social links + legal. Use `border-t border-gray-800` separator.
|
||||
|
||||
---
|
||||
|
||||
## SEO Checklist
|
||||
|
||||
- [ ] `<title>` tag: primary keyword + brand (50–60 chars)
|
||||
- [ ] Meta description: benefit + CTA (150–160 chars)
|
||||
- [ ] OG image: 1200×630px with product name and tagline
|
||||
- [ ] H1: one per page, includes primary keyword
|
||||
- [ ] Structured data: FAQPage, Product, or Organization schema
|
||||
- [ ] Canonical URL set
|
||||
- [ ] Image alt text on all `<Image>` components
|
||||
- [ ] robots.txt and sitemap.xml configured
|
||||
- [ ] Core Web Vitals: LCP < 1s, CLS < 0.1
|
||||
- [ ] Mobile viewport meta tag present
|
||||
- [ ] Internal linking to pricing and docs
|
||||
|
||||
> **Validation step:** Before outputting final code, verify every checklist item above is satisfied. Fix any gaps inline — do not skip items.
|
||||
|
||||
---
|
||||
|
||||
## Performance Targets
|
||||
|
||||
| Metric | Target | Technique |
|
||||
|---|---|---|
|
||||
| LCP | < 1s | Preload hero image, use `priority` on Next/Image |
|
||||
| CLS | < 0.1 | Set explicit width/height on all images |
|
||||
| FID/INP | < 100ms | Defer non-critical JS, use `loading="lazy"` |
|
||||
| TTFB | < 200ms | Use ISR or static generation for landing pages |
|
||||
| Bundle | < 100KB JS | Audit with `@next/bundle-analyzer` |
|
||||
|
||||
---
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Hero image not preloaded — add `priority` prop to first `<Image>`
|
||||
- Missing mobile breakpoints — always design mobile-first with `sm:` prefixes
|
||||
- CTA copy too vague — "Get started" beats "Learn more"; "Start free trial" beats "Sign up"
|
||||
- Pricing page missing trust signals — add money-back guarantee and testimonials near CTA
|
||||
- No above-the-fold CTA on mobile — ensure button is visible without scrolling on 375px viewport
|
||||
|
||||
---
|
||||
|
||||
## Related Skills
|
||||
|
||||
- **Brand Voice Analyzer** (`marketing-skill/content-production/scripts/brand_voice_analyzer.py`) — Run before generation to establish voice profile and ensure copy consistency
|
||||
- **UI Design System** (`product-team/ui-design-system/`) — Generate design tokens from brand color before building the page
|
||||
- **Competitive Teardown** (`product-team/competitive-teardown/`) — Competitive positioning informs landing page messaging and differentiation
|
||||
176
skills/landing-page-generator/references/conversion-patterns.md
Normal file
176
skills/landing-page-generator/references/conversion-patterns.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# High-Converting Landing Page Patterns
|
||||
|
||||
## Overview
|
||||
|
||||
This reference catalogs proven landing page design patterns that drive higher conversion rates. Each pattern includes placement guidance, implementation notes, and A/B testing priorities.
|
||||
|
||||
## Hero Section Layouts
|
||||
|
||||
### Pattern 1: Left Copy + Right Product Screenshot
|
||||
- **Best for:** SaaS products with a strong visual UI
|
||||
- **Structure:** Headline, subheadline, CTA on left (60%); product screenshot on right (40%)
|
||||
- **Why it works:** F-pattern reading leads with copy, product image provides proof
|
||||
- **Conversion lift:** Baseline pattern, strong performer across industries
|
||||
|
||||
### Pattern 2: Centered Copy + Full-Width Background
|
||||
- **Best for:** Brand-driven products, consumer apps
|
||||
- **Structure:** Centered headline, subheadline, CTA over background image/gradient
|
||||
- **Why it works:** Focuses attention on single message, high visual impact
|
||||
- **Note:** Ensure text contrast against background for readability
|
||||
|
||||
### Pattern 3: Video Hero
|
||||
- **Best for:** Complex products requiring demonstration
|
||||
- **Structure:** Short headline + embedded video (60-90 seconds) + CTA below
|
||||
- **Why it works:** Video explains what text cannot, increases time on page
|
||||
- **Note:** Always include thumbnail; autoplay is often counterproductive
|
||||
|
||||
### Pattern 4: Interactive Demo
|
||||
- **Best for:** Developer tools, data products, design tools
|
||||
- **Structure:** Minimal copy + embedded interactive product experience
|
||||
- **Why it works:** Hands-on experience converts better than description
|
||||
- **Note:** Keep demo focused on one "aha moment" workflow
|
||||
|
||||
## Social Proof Placement
|
||||
|
||||
### Logo Bar
|
||||
- **Position:** Immediately below hero section
|
||||
- **Count:** 5-7 logos for credibility without clutter
|
||||
- **Label:** "Trusted by" or "Used by teams at"
|
||||
- **Selection:** Mix recognizable brands with relevant industry logos
|
||||
|
||||
### Testimonial Cards
|
||||
- **Position:** After feature explanation sections
|
||||
- **Format:** Photo + name + title + company + specific quote
|
||||
- **Best quotes:** Include measurable outcomes ("Saved 10 hours/week")
|
||||
- **Layout:** 2-3 testimonials in a row, carousel for more
|
||||
|
||||
### Case Study Callouts
|
||||
- **Position:** Mid-page, before pricing
|
||||
- **Format:** Company logo + headline metric + "Read the story" link
|
||||
- **Example:** "Acme Corp reduced onboarding time by 60%"
|
||||
|
||||
### Social Proof Numbers
|
||||
- **Position:** Near CTA or in dedicated trust section
|
||||
- **Format:** Large number + descriptor (e.g., "50,000+ teams", "4.8/5 rating")
|
||||
- **Selection:** Choose 3-4 most impressive metrics
|
||||
|
||||
## Pricing Table Designs
|
||||
|
||||
### Good/Better/Best (3-Tier)
|
||||
- Most effective for SaaS with clear feature tiers
|
||||
- Highlight recommended plan with visual emphasis
|
||||
- Show annual discount prominently
|
||||
- Include feature comparison matrix below
|
||||
|
||||
### Simple Two-Tier
|
||||
- Free/Pro or Starter/Professional
|
||||
- Best for PLG products with clear upgrade trigger
|
||||
- Minimize decision fatigue
|
||||
|
||||
### Enterprise Custom
|
||||
- Replace price with "Contact Sales" for high-ACV products
|
||||
- List enterprise-specific features (SSO, SLA, dedicated support)
|
||||
- Include a "Talk to Sales" CTA, not just a form
|
||||
|
||||
### Pricing Psychology
|
||||
- Anchor with highest-priced plan first (or in the middle with visual highlight)
|
||||
- Use monthly price with annual billing toggle
|
||||
- Show savings percentage for annual plans
|
||||
- Round prices ending in 9 (e.g., $49/mo, $99/mo)
|
||||
|
||||
## Trust Signals
|
||||
|
||||
### Security Badges
|
||||
- SOC 2, ISO 27001, GDPR compliance badges
|
||||
- SSL certificate indicator
|
||||
- Place near forms and payment sections
|
||||
|
||||
### Guarantees
|
||||
- Money-back guarantee with specific timeframe
|
||||
- Free trial with no credit card requirement
|
||||
- SLA uptime commitments
|
||||
|
||||
### Awards & Recognition
|
||||
- Industry awards (best of, top rated)
|
||||
- Analyst recognition (Gartner, Forrester, G2 Leader)
|
||||
- Media mentions (as seen in logos)
|
||||
|
||||
### Real-Time Activity
|
||||
- "X people signed up today" (use real data only)
|
||||
- Recent activity feed
|
||||
- Live user count
|
||||
|
||||
## Urgency Elements
|
||||
|
||||
### Ethical Urgency
|
||||
- Limited-time pricing (with real deadline)
|
||||
- Early adopter benefits (extra features, lower price)
|
||||
- Cohort-based enrollment (actual capacity limits)
|
||||
|
||||
### Avoid
|
||||
- Fake countdown timers that reset
|
||||
- False scarcity ("only 3 left" when unlimited)
|
||||
- Pressure tactics that erode trust
|
||||
|
||||
## Form Optimization
|
||||
|
||||
### Field Reduction
|
||||
- Every additional field reduces conversion ~10%
|
||||
- Start with email only, progressive profiling later
|
||||
- Use single-column layouts for forms
|
||||
|
||||
### Smart Defaults
|
||||
- Pre-fill country based on IP
|
||||
- Auto-detect company from email domain
|
||||
- Default to most popular plan
|
||||
|
||||
### Inline Validation
|
||||
- Validate fields on blur, not on submit
|
||||
- Show success states (green checkmark)
|
||||
- Provide helpful error messages
|
||||
|
||||
### Multi-Step Forms
|
||||
- Break long forms into 2-3 steps with progress indicator
|
||||
- Put easiest questions first to build commitment
|
||||
- Allow saving progress for complex forms
|
||||
|
||||
## Mobile-First Patterns
|
||||
|
||||
### Thumb-Friendly Design
|
||||
- CTAs in thumb zone (bottom 40% of screen)
|
||||
- Minimum tap target: 44x44px
|
||||
- Adequate spacing between interactive elements
|
||||
|
||||
### Content Priority
|
||||
- Lead with most compelling content (no scrolling to find CTA)
|
||||
- Collapse secondary information into accordions
|
||||
- Use sticky CTA bar on scroll
|
||||
|
||||
### Performance
|
||||
- Target <3s load time on 3G
|
||||
- Lazy-load images below fold
|
||||
- Minimize JavaScript execution
|
||||
|
||||
## A/B Testing Priority Matrix
|
||||
|
||||
Test these elements in order of expected impact:
|
||||
|
||||
| Priority | Element | Expected Impact | Effort |
|
||||
|----------|---------|----------------|--------|
|
||||
| 1 | Headline | High | Low |
|
||||
| 2 | CTA text and color | High | Low |
|
||||
| 3 | Hero image/video | High | Medium |
|
||||
| 4 | Social proof placement | Medium | Low |
|
||||
| 5 | Form fields (fewer) | Medium | Low |
|
||||
| 6 | Pricing presentation | Medium | Medium |
|
||||
| 7 | Page length | Medium | High |
|
||||
| 8 | Testimonial selection | Low | Low |
|
||||
| 9 | Color scheme | Low | Medium |
|
||||
| 10 | Font choices | Low | Low |
|
||||
|
||||
### Testing Best Practices
|
||||
- Test one variable at a time for clear attribution
|
||||
- Run tests for minimum 2 weeks or 1,000 visitors per variant
|
||||
- Use 95% statistical significance threshold
|
||||
- Document all test results for institutional knowledge
|
||||
- Winner becomes new control for next test iteration
|
||||
177
skills/landing-page-generator/references/frameworks.md
Normal file
177
skills/landing-page-generator/references/frameworks.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# Landing Page Copywriting Frameworks
|
||||
|
||||
## Overview
|
||||
|
||||
Four copy frameworks with worked SaaS examples you can adapt. Each framework includes a complete before/after example plus specific guidelines for each section.
|
||||
|
||||
## 1. AIDA Framework (Attention - Interest - Desire - Action)
|
||||
|
||||
The classic direct response formula, ideal for product landing pages.
|
||||
|
||||
**Example — Project management SaaS:**
|
||||
|
||||
> **Attention:** "Your Team Loses 12 Hours Every Sprint to Status Meetings"
|
||||
>
|
||||
> **Interest:** "Engineering teams at Series A-C startups spend 23% of their week in sync meetings — not writing code. We tracked 847 teams over 6 months. The pattern was clear: the more people in a standup, the less code shipped that day."
|
||||
>
|
||||
> **Desire:** "Teams using AsyncStand ship 31% more story points per sprint. No more 15-person standups where 13 people zone out. Replace your daily sync with a 2-minute async check-in that your engineers actually complete (94% response rate vs 67% attendance for live standups)."
|
||||
>
|
||||
> **Action:** "Start Your Free 14-Day Trial — No Credit Card Required"
|
||||
|
||||
### Attention
|
||||
- Lead with a specific, quantified pain point (not vague claims)
|
||||
- Weak: "Save time on meetings" → Strong: "Your Team Loses 12 Hours Every Sprint to Status Meetings"
|
||||
- Keep headlines under 10 words for maximum impact
|
||||
|
||||
### Interest
|
||||
- Back up the headline with specific data or a relatable scenario
|
||||
- Weak: "Meetings waste time" → Strong: "We tracked 847 teams — the more people in standup, the less code shipped that day"
|
||||
- Use their language: mirror words from customer reviews, support tickets, and G2 feedback
|
||||
|
||||
### Desire
|
||||
- Stack measurable outcomes, not features
|
||||
- Weak: "AI-powered async updates" → Strong: "31% more story points per sprint, 94% response rate"
|
||||
- Compare directly to the status quo they already endure
|
||||
|
||||
### Action
|
||||
- Single, clear CTA with action-oriented verb
|
||||
- Reduce friction: "No credit card required," "Set up in 2 minutes"
|
||||
- Repeat CTA after each major content block
|
||||
|
||||
## 2. PAS Framework (Problem - Agitate - Solution)
|
||||
|
||||
Best for pain-point-driven products where the problem is well understood.
|
||||
|
||||
**Example — Expense management tool:**
|
||||
|
||||
> **Problem:** "Your finance team is still chasing receipts in Slack DMs."
|
||||
>
|
||||
> **Agitate:** "Last quarter, your team spent 46 hours manually reconciling expenses across email threads, shared drives, and 'I'll submit it later' promises. That's $4,200 in payroll — spent on data entry. And when audit season hits? Good luck finding that client dinner receipt from February."
|
||||
>
|
||||
> **Solution:** "Snap a photo of the receipt. ExpenseFlow auto-extracts vendor, amount, and category in 3 seconds. Your monthly close drops from 5 days to 1. 2,400 finance teams already made the switch."
|
||||
|
||||
### Problem
|
||||
- Name the exact scenario (not the abstract category)
|
||||
- Weak: "Expense tracking is hard" → Strong: "Your finance team is still chasing receipts in Slack DMs"
|
||||
- Mirror language from reviews and support tickets
|
||||
|
||||
### Agitate
|
||||
- Quantify the cost in dollars, hours, or missed opportunities
|
||||
- Weak: "This costs you money" → Strong: "46 hours last quarter, $4,200 in payroll — on data entry"
|
||||
- Acknowledge the workarounds they've tried and why those fail too
|
||||
|
||||
### Solution
|
||||
- Lead with the user action, not the technology: "Snap a photo" not "AI-powered OCR"
|
||||
- Include one proof point: number of customers, time saved, or before/after metric
|
||||
- Make the mechanism clear in one sentence: what happens when they use it
|
||||
|
||||
## 3. BAB Framework (Before - After - Bridge)
|
||||
|
||||
Ideal for aspirational products and lifestyle-oriented landing pages.
|
||||
|
||||
**Example — Sales enablement platform:**
|
||||
|
||||
> **Before:** "It's 9 PM. You're rebuilding a deck for tomorrow's demo because the prospect is in healthcare, not fintech. You copy-paste from three old decks, pray the logos are right, and rehearse the new talk track in the shower."
|
||||
>
|
||||
> **After:** "It's 9 AM. You type 'healthcare, 200-bed hospital, HIPAA-concerned CTO.' DeckGen builds your slides in 40 seconds — case studies, compliance badges, ROI calculator pre-loaded. You walk into the call with the best deck your prospect has ever seen."
|
||||
>
|
||||
> **Bridge:** "DeckGen connects to your CRM, learns your win patterns, and generates prospect-specific decks in under a minute. 340 AEs at companies like Stripe and Notion already use it. Start free — your first 5 decks are on us."
|
||||
|
||||
### Before
|
||||
- Describe a specific, lived moment — not an abstract pain category
|
||||
- Weak: "Sales decks take too long" → Strong: "It's 9 PM. You're rebuilding a deck for tomorrow's demo..."
|
||||
- Use second person and present tense to make it feel immediate
|
||||
|
||||
### After
|
||||
- Same level of specificity — show the transformed version of that exact moment
|
||||
- Include a measurable outcome: "40 seconds," "best deck your prospect has ever seen"
|
||||
- The after state should feel effortless compared to the before
|
||||
|
||||
### Bridge
|
||||
- Name the product explicitly and explain the mechanism in one sentence
|
||||
- Include one social proof data point
|
||||
- End with a low-friction CTA that connects to the after state
|
||||
|
||||
## 4. 4Ps Framework (Promise - Picture - Proof - Push)
|
||||
|
||||
Strong for SaaS and B2B landing pages with measurable outcomes.
|
||||
|
||||
### Promise
|
||||
- Make a clear, specific, believable promise
|
||||
- Tie it to a measurable outcome
|
||||
- Example: "Reduce customer churn by 25% in 90 days"
|
||||
|
||||
### Picture
|
||||
- Help the reader visualize success
|
||||
- Use scenarios they can relate to
|
||||
- Show the product in context (screenshots, demos)
|
||||
|
||||
### Proof
|
||||
- Back the promise with evidence
|
||||
- Customer testimonials with specific results
|
||||
- Case studies with before/after metrics
|
||||
- Third-party validation (awards, analyst reports)
|
||||
|
||||
### Push
|
||||
- Give a compelling reason to act now
|
||||
- Limited-time offer, bonus, or guarantee
|
||||
- Risk reversal (money-back guarantee, free trial)
|
||||
|
||||
## Headline Formulas
|
||||
|
||||
### Benefit-Driven
|
||||
- "Get [Desired Outcome] Without [Common Objection]"
|
||||
- "[Specific Result] in [Timeframe]"
|
||||
- "The [Adjective] Way to [Achieve Goal]"
|
||||
|
||||
### Problem-Driven
|
||||
- "Stop [Painful Activity]. Start [Better Alternative]."
|
||||
- "Tired of [Problem]? There's a Better Way."
|
||||
- "[Problem]? Not Anymore."
|
||||
|
||||
### Social Proof-Driven
|
||||
- "[Number] Teams Trust [Product] to [Outcome]"
|
||||
- "Why [Notable Company] Switched to [Product]"
|
||||
- "Rated #1 for [Category] by [Authority]"
|
||||
|
||||
### Question-Driven
|
||||
- "What If You Could [Desirable Outcome]?"
|
||||
- "Ready to [Transformation]?"
|
||||
- "Still [Painful Status Quo]?"
|
||||
|
||||
## CTA Best Practices
|
||||
|
||||
### Language
|
||||
- Use first-person: "Start My Free Trial" > "Start Your Free Trial"
|
||||
- Be specific: "Get My Report" > "Submit"
|
||||
- Include benefit: "Start Saving Time" > "Sign Up"
|
||||
- Add urgency naturally: "Start Free Today" > "Sign Up Now!!!"
|
||||
|
||||
### Placement
|
||||
- Primary CTA above the fold
|
||||
- Repeat after each major content section
|
||||
- Sticky CTA on scroll (mobile especially)
|
||||
- Exit-intent as last chance
|
||||
|
||||
### Design
|
||||
- High contrast color (stands out from page palette)
|
||||
- Sufficient whitespace around the button
|
||||
- Large enough to tap on mobile (min 44x44px)
|
||||
- Micro-copy below button to reduce anxiety ("No credit card required")
|
||||
|
||||
## Above-the-Fold Principles
|
||||
|
||||
The first viewport must accomplish these goals within 5 seconds:
|
||||
1. **Communicate what you do** - Clear, jargon-free headline
|
||||
2. **Show who it's for** - Audience identification
|
||||
3. **Demonstrate value** - Primary benefit or outcome
|
||||
4. **Provide next step** - Visible CTA button
|
||||
5. **Build credibility** - One trust signal (logo bar, metric, badge)
|
||||
|
||||
### Above-the-Fold Checklist
|
||||
- [ ] Headline states primary benefit (under 10 words)
|
||||
- [ ] Subheadline adds specificity or addresses objection
|
||||
- [ ] Hero image/video shows product in use
|
||||
- [ ] CTA button is visible without scrolling
|
||||
- [ ] At least one trust signal present
|
||||
- [ ] No jargon or ambiguity in messaging
|
||||
@@ -0,0 +1,98 @@
|
||||
# Landing Page Patterns
|
||||
|
||||
This reference captures high-converting page patterns and copy structures.
|
||||
|
||||
## Hero Section Patterns
|
||||
|
||||
### Pattern 1: Problem-Solution Hero
|
||||
- Headline names the painful problem.
|
||||
- Subheadline states the clear outcome.
|
||||
- Primary CTA starts immediately.
|
||||
- Optional supporting visual demonstrates product in context.
|
||||
|
||||
### Pattern 2: Outcome-First Hero
|
||||
- Headline leads with measurable value.
|
||||
- Subheadline clarifies who the page is for.
|
||||
- CTA is action-oriented and specific.
|
||||
|
||||
### Pattern 3: Authority Hero
|
||||
- Headline + trust indicator (logos, testimonial snippet, proof metric).
|
||||
- Useful when category skepticism is high.
|
||||
|
||||
## Social Proof Layouts
|
||||
|
||||
### Logo Strip + Proof Metric
|
||||
- Keep to recognizable logos.
|
||||
- Add one proof metric (e.g., active users, revenue saved, hours reduced).
|
||||
|
||||
### Testimonial Grid
|
||||
- 3-6 testimonials across segments.
|
||||
- Include role/company where possible.
|
||||
- Prefer concrete outcomes over generic praise.
|
||||
|
||||
### Case Study Snapshot
|
||||
- Mini blocks: challenge -> approach -> measurable result.
|
||||
|
||||
## CTA Best Practices
|
||||
|
||||
- Use one dominant CTA per section.
|
||||
- Match CTA verb to user intent ("Start trial", "Get demo", "Run audit").
|
||||
- Keep CTA copy specific; avoid vague labels like "Submit".
|
||||
- Reduce friction near CTA (short form, trust indicators, no surprise commitments).
|
||||
|
||||
## Above-the-Fold Checklist
|
||||
|
||||
- [ ] Clear value proposition in first viewport
|
||||
- [ ] Audience clarity (who this is for)
|
||||
- [ ] One primary CTA visible without scrolling
|
||||
- [ ] Proof element (logos, stat, quote)
|
||||
- [ ] Visual hierarchy emphasizes headline + CTA
|
||||
- [ ] Mobile layout keeps CTA accessible
|
||||
|
||||
## Conversion-Optimized Templates
|
||||
|
||||
### SaaS Demo Page
|
||||
1. Hero with problem-solution framing
|
||||
2. Product walkthrough section
|
||||
3. Social proof strip
|
||||
4. Benefits by persona
|
||||
5. Objection handling FAQ
|
||||
6. Final CTA
|
||||
|
||||
### Lead Magnet Page
|
||||
1. Promise + asset preview
|
||||
2. Bullet outcomes
|
||||
3. Short form
|
||||
4. Trust/privacy note
|
||||
|
||||
### Product Launch Page
|
||||
1. Outcome-first hero
|
||||
2. Why now / differentiation
|
||||
3. Feature blocks
|
||||
4. Testimonials / beta feedback
|
||||
5. Pricing or waitlist CTA
|
||||
|
||||
## Headline Formulas
|
||||
|
||||
### PAS (Problem-Agitate-Solution)
|
||||
- Problem: identify the pain
|
||||
- Agitate: show consequences of inaction
|
||||
- Solution: position the offer as relief
|
||||
|
||||
Example structure:
|
||||
"Still [problem]? Stop [negative consequence] and start [desired outcome]."
|
||||
|
||||
### AIDA (Attention-Interest-Desire-Action)
|
||||
- Attention: pattern interrupt headline
|
||||
- Interest: relevant context and stakes
|
||||
- Desire: proof and benefits
|
||||
- Action: concrete next step
|
||||
|
||||
### 4U Formula
|
||||
- Useful: clear practical value
|
||||
- Urgent: reason to act now
|
||||
- Unique: differentiated promise
|
||||
- Ultra-specific: concrete outcome and scope
|
||||
|
||||
Example structure:
|
||||
"Get [specific result] in [timeframe] without [common pain]."
|
||||
109
skills/landing-page-generator/references/seo-checklist.md
Normal file
109
skills/landing-page-generator/references/seo-checklist.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Landing Page SEO Checklist
|
||||
|
||||
## Overview
|
||||
|
||||
This checklist ensures landing pages are optimized for search engine visibility while maintaining conversion focus. Apply these checks before launching any landing page.
|
||||
|
||||
## Meta Tags
|
||||
|
||||
- [ ] **Title tag**: Under 60 characters, includes primary keyword, ends with brand name
|
||||
- [ ] **Meta description**: 150-160 characters, includes CTA language, unique per page
|
||||
- [ ] **Canonical URL**: Set to prevent duplicate content issues
|
||||
- [ ] **Robots meta**: Ensure page is indexable (`index, follow`) unless intentionally noindex
|
||||
- [ ] **Open Graph tags**: og:title, og:description, og:image, og:url for social sharing
|
||||
- [ ] **Twitter Card tags**: twitter:card, twitter:title, twitter:description, twitter:image
|
||||
- [ ] **Viewport meta**: `<meta name="viewport" content="width=device-width, initial-scale=1">`
|
||||
|
||||
## Structured Data
|
||||
|
||||
- [ ] **Organization schema**: Company name, logo, social profiles
|
||||
- [ ] **Product schema**: Name, description, price, availability (for product pages)
|
||||
- [ ] **FAQ schema**: For pages with FAQ sections (rich snippet opportunity)
|
||||
- [ ] **Breadcrumb schema**: Navigation path for deep pages
|
||||
- [ ] **Review schema**: Aggregate rating if testimonials present (use carefully per guidelines)
|
||||
- [ ] **Validate**: Test all structured data with Google Rich Results Test
|
||||
|
||||
## Core Web Vitals Targets
|
||||
|
||||
### Largest Contentful Paint (LCP) - Target: < 2.5s
|
||||
- [ ] Optimize hero image (WebP format, proper dimensions)
|
||||
- [ ] Preload critical resources (`<link rel="preload">`)
|
||||
- [ ] Use CDN for static assets
|
||||
- [ ] Minimize render-blocking CSS and JavaScript
|
||||
|
||||
### First Input Delay (FID) / Interaction to Next Paint (INP) - Target: < 200ms
|
||||
- [ ] Defer non-critical JavaScript
|
||||
- [ ] Break up long tasks (>50ms)
|
||||
- [ ] Minimize third-party script impact
|
||||
- [ ] Use `requestAnimationFrame` for visual updates
|
||||
|
||||
### Cumulative Layout Shift (CLS) - Target: < 0.1
|
||||
- [ ] Set explicit width/height on images and videos
|
||||
- [ ] Reserve space for dynamic content (ads, embeds)
|
||||
- [ ] Use `font-display: swap` for web fonts
|
||||
- [ ] Avoid inserting content above existing content
|
||||
|
||||
## Keyword Placement
|
||||
|
||||
- [ ] **H1 tag**: Contains primary keyword, one per page only
|
||||
- [ ] **H2 tags**: Include secondary keywords naturally
|
||||
- [ ] **First paragraph**: Primary keyword appears in first 100 words
|
||||
- [ ] **Body copy**: Natural keyword density (1-2%), no stuffing
|
||||
- [ ] **Image alt text**: Descriptive, includes keyword where relevant
|
||||
- [ ] **URL slug**: Short, keyword-rich, hyphen-separated
|
||||
- [ ] **CTA text**: Consider keyword inclusion where natural
|
||||
|
||||
## Internal Linking
|
||||
|
||||
- [ ] Link to relevant product/feature pages
|
||||
- [ ] Link to blog content that supports the page topic
|
||||
- [ ] Use descriptive anchor text (not "click here")
|
||||
- [ ] Ensure landing page is linked from main navigation or sitemap
|
||||
- [ ] Link to pricing page if applicable
|
||||
- [ ] Limit links to avoid diluting page authority (15-20 max)
|
||||
|
||||
## Image Optimization
|
||||
|
||||
- [ ] **Format**: Use WebP with JPEG/PNG fallback
|
||||
- [ ] **Compression**: Lossy compression for photos, lossless for graphics
|
||||
- [ ] **Dimensions**: Serve at exact display size (no CSS resizing)
|
||||
- [ ] **Alt text**: Descriptive, 125 characters max, natural keyword inclusion
|
||||
- [ ] **File names**: Descriptive, hyphenated (e.g., `product-dashboard-screenshot.webp`)
|
||||
- [ ] **Lazy loading**: Apply to images below the fold (`loading="lazy"`)
|
||||
- [ ] **Responsive images**: Use `srcset` for different viewport sizes
|
||||
|
||||
## Canonical URLs
|
||||
|
||||
- [ ] Self-referencing canonical on every page
|
||||
- [ ] Consistent protocol (https) and trailing slash usage
|
||||
- [ ] Canonical points to preferred URL version (www vs non-www)
|
||||
- [ ] UTM parameters excluded from canonical URL
|
||||
- [ ] Pagination handled with rel="next"/"prev" or single-page canonical
|
||||
|
||||
## Mobile Responsiveness
|
||||
|
||||
- [ ] **Mobile-friendly test**: Pass Google Mobile-Friendly Test
|
||||
- [ ] **Touch targets**: Minimum 44x44px, 8px spacing between targets
|
||||
- [ ] **Font size**: Minimum 16px base font, no pinch-to-zoom needed
|
||||
- [ ] **Content parity**: All critical content accessible on mobile
|
||||
- [ ] **Horizontal scroll**: None present at any viewport width
|
||||
- [ ] **Form usability**: Appropriate input types (email, tel), autocomplete attributes
|
||||
- [ ] **Media queries**: Breakpoints at 480px, 768px, 1024px, 1200px minimum
|
||||
|
||||
## Technical SEO
|
||||
|
||||
- [ ] **HTTPS**: SSL certificate valid and active
|
||||
- [ ] **Page speed**: < 3s load time on mobile (test with PageSpeed Insights)
|
||||
- [ ] **XML sitemap**: Page included in sitemap.xml
|
||||
- [ ] **Robots.txt**: Page not blocked by robots.txt
|
||||
- [ ] **404 handling**: Custom 404 page with navigation
|
||||
- [ ] **Redirect chains**: No more than 1 redirect hop
|
||||
- [ ] **Hreflang**: Set for multi-language landing pages
|
||||
|
||||
## Content Quality Signals
|
||||
|
||||
- [ ] **Unique content**: No duplicate content from other pages
|
||||
- [ ] **Content depth**: Sufficient content for topic coverage (500+ words for SEO pages)
|
||||
- [ ] **Readability**: Grade level 6-8 for broad audiences
|
||||
- [ ] **Freshness**: Last modified date reflects recent updates
|
||||
- [ ] **E-E-A-T signals**: Author expertise, company authority, trust indicators
|
||||
568
skills/landing-page-generator/scripts/landing_page_scaffolder.py
Normal file
568
skills/landing-page-generator/scripts/landing_page_scaffolder.py
Normal file
@@ -0,0 +1,568 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Landing Page Scaffolder — Generate landing pages as HTML or Next.js TSX from config.
|
||||
|
||||
Creates production-ready landing pages with hero sections, features,
|
||||
testimonials, pricing, CTAs, and responsive design.
|
||||
|
||||
Usage:
|
||||
python landing_page_scaffolder.py config.json --format html --output page.html
|
||||
python landing_page_scaffolder.py config.json --format tsx --output LandingPage.tsx
|
||||
python landing_page_scaffolder.py config.json --format json
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from typing import Dict, List, Any, Optional
|
||||
from datetime import datetime
|
||||
import html as html_module
|
||||
|
||||
|
||||
def escape(text: str) -> str:
|
||||
"""HTML-escape text."""
|
||||
return html_module.escape(str(text))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tailwind style mappings for TSX output
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
DESIGN_STYLES = {
|
||||
"dark-saas": {
|
||||
"bg": "bg-gray-950", "text": "text-white",
|
||||
"accent": "violet", "card_bg": "bg-gray-900 border border-gray-800",
|
||||
"btn": "bg-violet-600 hover:bg-violet-500 text-white",
|
||||
"btn_secondary": "border border-gray-700 text-gray-300 hover:bg-gray-800",
|
||||
"section_alt": "bg-gray-900/50", "muted": "text-gray-400",
|
||||
"border": "border-gray-800",
|
||||
},
|
||||
"clean-minimal": {
|
||||
"bg": "bg-white", "text": "text-gray-900",
|
||||
"accent": "blue", "card_bg": "bg-gray-50 border border-gray-200 rounded-2xl",
|
||||
"btn": "bg-blue-600 hover:bg-blue-700 text-white",
|
||||
"btn_secondary": "border border-gray-300 text-gray-700 hover:bg-gray-50",
|
||||
"section_alt": "bg-gray-50", "muted": "text-gray-500",
|
||||
"border": "border-gray-200",
|
||||
},
|
||||
"bold-startup": {
|
||||
"bg": "bg-white", "text": "text-gray-900",
|
||||
"accent": "orange", "card_bg": "shadow-xl rounded-3xl bg-white",
|
||||
"btn": "bg-orange-500 hover:bg-orange-600 text-white",
|
||||
"btn_secondary": "border-2 border-orange-500 text-orange-600 hover:bg-orange-50",
|
||||
"section_alt": "bg-orange-50/30", "muted": "text-gray-500",
|
||||
"border": "border-gray-200",
|
||||
},
|
||||
"enterprise": {
|
||||
"bg": "bg-slate-50", "text": "text-slate-900",
|
||||
"accent": "slate", "card_bg": "bg-white border border-slate-200 shadow-sm",
|
||||
"btn": "bg-slate-900 hover:bg-slate-800 text-white",
|
||||
"btn_secondary": "border border-slate-300 text-slate-700 hover:bg-slate-100",
|
||||
"section_alt": "bg-white", "muted": "text-slate-500",
|
||||
"border": "border-slate-200",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TSX generators
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def tsx_nav(config: Dict[str, Any], style: Dict[str, str]) -> str:
|
||||
brand = config.get("brand", "Brand")
|
||||
nav_links = config.get("nav_links", [])
|
||||
cta = config.get("nav_cta", {"text": "Get Started", "url": "#"})
|
||||
links_jsx = "\n ".join(
|
||||
f'<a href="{l.get("url", "#")}" className="{style["muted"]} hover:{style["text"]} font-medium transition-colors">{l.get("text", "")}</a>'
|
||||
for l in nav_links
|
||||
)
|
||||
return f'''function Navbar() {{
|
||||
return (
|
||||
<nav className="sticky top-0 z-50 {style["bg"]} border-b {style["border"]} backdrop-blur-sm">
|
||||
<div className="mx-auto flex max-w-7xl items-center justify-between px-6 py-4">
|
||||
<a href="#" className="text-xl font-bold {style["text"]}">{brand}</a>
|
||||
<div className="hidden items-center gap-8 md:flex">
|
||||
{links_jsx}
|
||||
<a href="{cta.get("url", "#")}" className="rounded-lg {style["btn"]} px-5 py-2.5 text-sm font-semibold transition-colors">
|
||||
{cta.get("text", "Get Started")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}}'''
|
||||
|
||||
|
||||
def tsx_hero(hero: Dict[str, Any], style: Dict[str, str]) -> str:
|
||||
h1 = hero.get("headline", "Your Headline Here")
|
||||
sub = hero.get("subheadline", "")
|
||||
primary_cta = hero.get("primary_cta", {"text": "Get Started", "url": "#"})
|
||||
secondary_cta = hero.get("secondary_cta", None)
|
||||
secondary_jsx = ""
|
||||
if secondary_cta:
|
||||
secondary_jsx = f'''
|
||||
<a href="{secondary_cta.get("url", "#")}" className="rounded-lg {style["btn_secondary"]} px-8 py-3 text-lg font-semibold transition-colors">
|
||||
{secondary_cta.get("text", "Learn More")}
|
||||
</a>'''
|
||||
return f'''function Hero() {{
|
||||
return (
|
||||
<section className="flex min-h-[80vh] flex-col items-center justify-center px-6 py-24 text-center {style["bg"]}">
|
||||
<div className="mx-auto max-w-4xl">
|
||||
<h1 className="mb-6 text-5xl font-bold tracking-tight {style["text"]} md:text-7xl">
|
||||
{h1}
|
||||
</h1>
|
||||
<p className="mx-auto mb-10 max-w-2xl text-xl {style["muted"]}">
|
||||
{sub}
|
||||
</p>
|
||||
<div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-center">
|
||||
<a href="{primary_cta.get("url", "#")}" className="rounded-lg {style["btn"]} px-8 py-3 text-lg font-semibold transition-colors">
|
||||
{primary_cta.get("text", "Get Started")}
|
||||
</a>{secondary_jsx}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}}'''
|
||||
|
||||
|
||||
def tsx_features(features: Dict[str, Any], style: Dict[str, str]) -> str:
|
||||
title = features.get("title", "Features")
|
||||
subtitle = features.get("subtitle", "")
|
||||
items = features.get("items", [])
|
||||
cards_jsx = "\n ".join(
|
||||
f'''<div className="{style["card_bg"]} rounded-xl p-8">
|
||||
<div className="mb-4 text-3xl">{f.get("icon", "")}</div>
|
||||
<h3 className="mb-3 text-xl font-semibold {style["text"]}">{f.get("title", "")}</h3>
|
||||
<p className="{style["muted"]}">{f.get("description", "")}</p>
|
||||
</div>'''
|
||||
for f in items
|
||||
)
|
||||
return f'''function Features() {{
|
||||
return (
|
||||
<section className="{style["section_alt"]} px-6 py-24">
|
||||
<div className="mx-auto max-w-7xl">
|
||||
<h2 className="mb-4 text-center text-4xl font-bold {style["text"]}">{title}</h2>
|
||||
<p className="mx-auto mb-16 max-w-2xl text-center text-lg {style["muted"]}">{subtitle}</p>
|
||||
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
{cards_jsx}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}}'''
|
||||
|
||||
|
||||
def tsx_testimonials(testimonials: Dict[str, Any], style: Dict[str, str]) -> str:
|
||||
title = testimonials.get("title", "What Our Customers Say")
|
||||
items = testimonials.get("items", [])
|
||||
if not items:
|
||||
return ""
|
||||
cards_jsx = "\n ".join(
|
||||
f'''<div className="rounded-xl border {style["border"]} p-8">
|
||||
<p className="mb-6 text-lg italic {style["muted"]}">"{t.get("quote", "")}"</p>
|
||||
<div>
|
||||
<p className="font-semibold {style["text"]}">{t.get("name", "")}</p>
|
||||
<p className="text-sm {style["muted"]}">{t.get("title", "")}, {t.get("company", "")}</p>
|
||||
</div>
|
||||
</div>'''
|
||||
for t in items
|
||||
)
|
||||
return f'''function Testimonials() {{
|
||||
return (
|
||||
<section className="px-6 py-24 {style["bg"]}">
|
||||
<div className="mx-auto max-w-7xl">
|
||||
<h2 className="mb-16 text-center text-4xl font-bold {style["text"]}">{title}</h2>
|
||||
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
{cards_jsx}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}}'''
|
||||
|
||||
|
||||
def tsx_pricing(pricing: Dict[str, Any], style: Dict[str, str]) -> str:
|
||||
title = pricing.get("title", "Pricing")
|
||||
plans = pricing.get("plans", [])
|
||||
if not plans:
|
||||
return ""
|
||||
accent = style["accent"]
|
||||
cards = []
|
||||
for p in plans:
|
||||
featured = p.get("featured", False)
|
||||
border_cls = f"border-2 border-{accent}-500 ring-4 ring-{accent}-500/20" if featured else f"border {style['border']}"
|
||||
badge = f'\n <div className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-{accent}-600 px-4 py-1 text-xs font-semibold text-white">Most Popular</div>' if featured else ""
|
||||
features_jsx = "\n ".join(
|
||||
f'<li className="flex items-center gap-2 py-2"><span className="text-{accent}-500 font-bold">✓</span> {feat}</li>'
|
||||
for feat in p.get("features", [])
|
||||
)
|
||||
cards.append(f'''<div className="relative rounded-2xl {border_cls} {style["card_bg"]} p-8 text-center">{badge}
|
||||
<h3 className="mb-2 text-xl font-semibold {style["text"]}">{p.get("name", "")}</h3>
|
||||
<div className="my-6 text-5xl font-extrabold {style["text"]}">${p.get("price", "0")}<span className="text-base font-normal {style["muted"]}">/mo</span></div>
|
||||
<p className="{style["muted"]} mb-6">{p.get("description", "")}</p>
|
||||
<ul className="mb-8 space-y-1 text-left {style["muted"]}">
|
||||
{features_jsx}
|
||||
</ul>
|
||||
<a href="{p.get("cta_url", "#")}" className="block w-full rounded-lg {style["btn"]} py-3 text-center font-semibold transition-colors">
|
||||
{p.get("cta_text", "Choose Plan")}
|
||||
</a>
|
||||
</div>''')
|
||||
cards_jsx = "\n ".join(cards)
|
||||
return f'''function Pricing() {{
|
||||
return (
|
||||
<section className="{style["section_alt"]} px-6 py-24">
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<h2 className="mb-16 text-center text-4xl font-bold {style["text"]}">{title}</h2>
|
||||
<div className="grid gap-8 lg:grid-cols-{min(len(plans), 3)}">
|
||||
{cards_jsx}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}}'''
|
||||
|
||||
|
||||
def tsx_cta(cta: Dict[str, Any], style: Dict[str, str]) -> str:
|
||||
accent = style["accent"]
|
||||
return f'''function CTASection() {{
|
||||
return (
|
||||
<section className="bg-{accent}-600 px-6 py-24 text-center text-white">
|
||||
<div className="mx-auto max-w-3xl">
|
||||
<h2 className="mb-4 text-4xl font-bold">{cta.get("headline", "Ready to get started?")}</h2>
|
||||
<p className="mb-10 text-xl opacity-90">{cta.get("subheadline", "")}</p>
|
||||
<a href="{cta.get("url", "#")}" className="rounded-lg bg-white px-8 py-3 text-lg font-semibold text-{accent}-600 transition-colors hover:bg-gray-100">
|
||||
{cta.get("text", "Start Free Trial")}
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}}'''
|
||||
|
||||
|
||||
def tsx_footer(config: Dict[str, Any], style: Dict[str, str]) -> str:
|
||||
brand = config.get("brand", "Company")
|
||||
year = datetime.now().year
|
||||
footer_text = config.get("footer_text", f"{year} {brand}. All rights reserved.")
|
||||
return f'''function Footer() {{
|
||||
return (
|
||||
<footer className="border-t {style["border"]} {style["bg"]} px-6 py-10 text-center {style["muted"]}">
|
||||
<p>© {footer_text}</p>
|
||||
</footer>
|
||||
);
|
||||
}}'''
|
||||
|
||||
|
||||
def generate_tsx(config: Dict[str, Any]) -> str:
|
||||
"""Generate complete Next.js/React TSX landing page with Tailwind CSS."""
|
||||
style_name = config.get("design_style", "clean-minimal")
|
||||
style = DESIGN_STYLES.get(style_name, DESIGN_STYLES["clean-minimal"])
|
||||
|
||||
components = []
|
||||
component_names = []
|
||||
|
||||
components.append(tsx_nav(config, style))
|
||||
component_names.append("Navbar")
|
||||
|
||||
if config.get("hero"):
|
||||
components.append(tsx_hero(config["hero"], style))
|
||||
component_names.append("Hero")
|
||||
|
||||
if config.get("features"):
|
||||
components.append(tsx_features(config["features"], style))
|
||||
component_names.append("Features")
|
||||
|
||||
if config.get("testimonials") and config["testimonials"].get("items"):
|
||||
components.append(tsx_testimonials(config["testimonials"], style))
|
||||
component_names.append("Testimonials")
|
||||
|
||||
if config.get("pricing") and config["pricing"].get("plans"):
|
||||
components.append(tsx_pricing(config["pricing"], style))
|
||||
component_names.append("Pricing")
|
||||
|
||||
if config.get("cta"):
|
||||
components.append(tsx_cta(config["cta"], style))
|
||||
component_names.append("CTASection")
|
||||
|
||||
components.append(tsx_footer(config, style))
|
||||
component_names.append("Footer")
|
||||
|
||||
title = config.get("title", "Landing Page")
|
||||
meta_desc = config.get("meta_description", "")
|
||||
|
||||
page_body = "\n ".join(f"<{name} />" for name in component_names)
|
||||
all_components = "\n\n".join(components)
|
||||
|
||||
return f'''// Generated by Landing Page Scaffolder — {datetime.now().strftime("%Y-%m-%d")}
|
||||
// Stack: Next.js 14+ App Router, React, Tailwind CSS
|
||||
// Design style: {style_name}
|
||||
|
||||
import type {{ Metadata }} from "next";
|
||||
|
||||
export const metadata: Metadata = {{
|
||||
title: "{title}",
|
||||
description: "{meta_desc}",
|
||||
openGraph: {{
|
||||
title: "{title}",
|
||||
description: "{meta_desc}",
|
||||
type: "website",
|
||||
}},
|
||||
}};
|
||||
|
||||
{all_components}
|
||||
|
||||
export default function LandingPage() {{
|
||||
return (
|
||||
<main>
|
||||
{page_body}
|
||||
</main>
|
||||
);
|
||||
}}
|
||||
'''
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# HTML generators (existing)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def generate_css(config: Dict[str, Any]) -> str:
|
||||
"""Generate responsive CSS from config theme."""
|
||||
theme = config.get("theme", {})
|
||||
primary = theme.get("primary_color", "#2563eb")
|
||||
secondary = theme.get("secondary_color", "#1e40af")
|
||||
bg = theme.get("background", "#ffffff")
|
||||
text_color = theme.get("text_color", "#1f2937")
|
||||
font = theme.get("font", "Inter, system-ui, -apple-system, sans-serif")
|
||||
|
||||
return f"""
|
||||
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
|
||||
body {{ font-family: {font}; color: {text_color}; background: {bg}; line-height: 1.6; }}
|
||||
.container {{ max-width: 1200px; margin: 0 auto; padding: 0 24px; }}
|
||||
nav {{ padding: 16px 0; border-bottom: 1px solid #e5e7eb; position: sticky; top: 0; background: {bg}; z-index: 100; }}
|
||||
nav .container {{ display: flex; justify-content: space-between; align-items: center; }}
|
||||
.nav-logo {{ font-size: 1.5rem; font-weight: 700; color: {primary}; text-decoration: none; }}
|
||||
.nav-links {{ display: flex; gap: 24px; list-style: none; }}
|
||||
.nav-links a {{ text-decoration: none; color: {text_color}; font-weight: 500; }}
|
||||
.nav-cta {{ background: {primary}; color: white; padding: 8px 20px; border-radius: 6px; text-decoration: none; font-weight: 600; }}
|
||||
.hero {{ padding: 80px 0; text-align: center; }}
|
||||
.hero h1 {{ font-size: 3.5rem; font-weight: 800; line-height: 1.1; margin-bottom: 24px; max-width: 800px; margin-left: auto; margin-right: auto; }}
|
||||
.hero p {{ font-size: 1.25rem; color: #6b7280; max-width: 600px; margin: 0 auto 32px; }}
|
||||
.hero-cta {{ display: inline-flex; gap: 16px; }}
|
||||
.btn-primary {{ background: {primary}; color: white; padding: 14px 32px; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 1.1rem; }}
|
||||
.btn-secondary {{ background: transparent; color: {primary}; padding: 14px 32px; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 1.1rem; border: 2px solid {primary}; }}
|
||||
.features {{ padding: 80px 0; background: #f9fafb; }}
|
||||
.section-title {{ text-align: center; font-size: 2.5rem; font-weight: 700; margin-bottom: 16px; }}
|
||||
.section-subtitle {{ text-align: center; color: #6b7280; font-size: 1.1rem; margin-bottom: 48px; max-width: 600px; margin-left: auto; margin-right: auto; }}
|
||||
.features-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px; }}
|
||||
.feature-card {{ background: white; padding: 32px; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }}
|
||||
.feature-icon {{ font-size: 2rem; margin-bottom: 16px; }}
|
||||
.feature-card h3 {{ font-size: 1.25rem; margin-bottom: 12px; }}
|
||||
.feature-card p {{ color: #6b7280; }}
|
||||
.testimonials {{ padding: 80px 0; }}
|
||||
.testimonials-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 24px; }}
|
||||
.testimonial-card {{ padding: 32px; border: 1px solid #e5e7eb; border-radius: 12px; }}
|
||||
.testimonial-text {{ font-size: 1.1rem; font-style: italic; margin-bottom: 20px; }}
|
||||
.testimonial-author {{ display: flex; align-items: center; gap: 12px; }}
|
||||
.author-info strong {{ display: block; }}
|
||||
.author-info span {{ color: #6b7280; font-size: 0.9rem; }}
|
||||
.pricing {{ padding: 80px 0; background: #f9fafb; }}
|
||||
.pricing-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 24px; max-width: 900px; margin: 0 auto; }}
|
||||
.pricing-card {{ background: white; padding: 32px; border-radius: 12px; border: 2px solid #e5e7eb; text-align: center; }}
|
||||
.pricing-card.featured {{ border-color: {primary}; position: relative; }}
|
||||
.pricing-card.featured::before {{ content: "Most Popular"; position: absolute; top: -12px; left: 50%; transform: translateX(-50%); background: {primary}; color: white; padding: 4px 16px; border-radius: 20px; font-size: 0.8rem; font-weight: 600; }}
|
||||
.pricing-name {{ font-size: 1.25rem; font-weight: 600; margin-bottom: 8px; }}
|
||||
.pricing-price {{ font-size: 3rem; font-weight: 800; margin: 16px 0; }}
|
||||
.pricing-price span {{ font-size: 1rem; font-weight: 400; color: #6b7280; }}
|
||||
.pricing-features {{ list-style: none; text-align: left; margin: 24px 0; }}
|
||||
.pricing-features li {{ padding: 8px 0; border-bottom: 1px solid #f3f4f6; }}
|
||||
.pricing-features li::before {{ content: "\\2713 "; color: {primary}; font-weight: 700; }}
|
||||
.cta-section {{ padding: 80px 0; text-align: center; background: {primary}; color: white; }}
|
||||
.cta-section h2 {{ font-size: 2.5rem; margin-bottom: 16px; }}
|
||||
.cta-section p {{ font-size: 1.1rem; opacity: 0.9; margin-bottom: 32px; }}
|
||||
.btn-white {{ background: white; color: {primary}; padding: 14px 32px; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 1.1rem; }}
|
||||
footer {{ padding: 40px 0; border-top: 1px solid #e5e7eb; color: #6b7280; text-align: center; }}
|
||||
@media (max-width: 768px) {{
|
||||
.hero h1 {{ font-size: 2.25rem; }}
|
||||
.hero-cta {{ flex-direction: column; align-items: center; }}
|
||||
.nav-links {{ display: none; }}
|
||||
.features-grid {{ grid-template-columns: 1fr; }}
|
||||
.pricing-grid {{ grid-template-columns: 1fr; }}
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def render_nav(config: Dict[str, Any]) -> str:
|
||||
brand = escape(config.get("brand", "Brand"))
|
||||
nav_links = config.get("nav_links", [])
|
||||
cta = config.get("nav_cta", {"text": "Get Started", "url": "#"})
|
||||
links = "\n".join(
|
||||
f'<li><a href="{escape(l.get("url", "#"))}">{escape(l.get("text", ""))}</a></li>'
|
||||
for l in nav_links
|
||||
)
|
||||
return f"""
|
||||
<nav><div class="container">
|
||||
<a href="#" class="nav-logo">{brand}</a>
|
||||
<ul class="nav-links">{links}</ul>
|
||||
<a href="{escape(cta.get('url', '#'))}" class="nav-cta">{escape(cta.get('text', 'Get Started'))}</a>
|
||||
</div></nav>"""
|
||||
|
||||
|
||||
def render_hero(hero: Dict[str, Any]) -> str:
|
||||
h1 = escape(hero.get("headline", "Your Headline Here"))
|
||||
sub = escape(hero.get("subheadline", ""))
|
||||
primary_cta = hero.get("primary_cta", {"text": "Get Started", "url": "#"})
|
||||
secondary_cta = hero.get("secondary_cta", None)
|
||||
cta_html = f'<a href="{escape(primary_cta.get("url", "#"))}" class="btn-primary">{escape(primary_cta.get("text", "Get Started"))}</a>'
|
||||
if secondary_cta:
|
||||
cta_html += f'\n<a href="{escape(secondary_cta.get("url", "#"))}" class="btn-secondary">{escape(secondary_cta.get("text", "Learn More"))}</a>'
|
||||
return f"""
|
||||
<section class="hero"><div class="container">
|
||||
<h1>{h1}</h1>
|
||||
<p>{sub}</p>
|
||||
<div class="hero-cta">{cta_html}</div>
|
||||
</div></section>"""
|
||||
|
||||
|
||||
def render_features(features: Dict[str, Any]) -> str:
|
||||
title = escape(features.get("title", "Features"))
|
||||
subtitle = escape(features.get("subtitle", ""))
|
||||
items = features.get("items", [])
|
||||
cards = "\n".join(f"""
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">{escape(f.get('icon', ''))}</div>
|
||||
<h3>{escape(f.get('title', ''))}</h3>
|
||||
<p>{escape(f.get('description', ''))}</p>
|
||||
</div>""" for f in items)
|
||||
return f"""
|
||||
<section class="features"><div class="container">
|
||||
<h2 class="section-title">{title}</h2>
|
||||
<p class="section-subtitle">{subtitle}</p>
|
||||
<div class="features-grid">{cards}</div>
|
||||
</div></section>"""
|
||||
|
||||
|
||||
def render_testimonials(testimonials: Dict[str, Any]) -> str:
|
||||
title = escape(testimonials.get("title", "What Our Customers Say"))
|
||||
items = testimonials.get("items", [])
|
||||
if not items:
|
||||
return ""
|
||||
cards = "\n".join(f"""
|
||||
<div class="testimonial-card">
|
||||
<p class="testimonial-text">"{escape(t.get('quote', ''))}"</p>
|
||||
<div class="testimonial-author">
|
||||
<div class="author-info">
|
||||
<strong>{escape(t.get('name', ''))}</strong>
|
||||
<span>{escape(t.get('title', ''))}, {escape(t.get('company', ''))}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>""" for t in items)
|
||||
return f"""
|
||||
<section class="testimonials"><div class="container">
|
||||
<h2 class="section-title">{title}</h2>
|
||||
<div class="testimonials-grid">{cards}</div>
|
||||
</div></section>"""
|
||||
|
||||
|
||||
def render_pricing(pricing: Dict[str, Any]) -> str:
|
||||
title = escape(pricing.get("title", "Pricing"))
|
||||
plans = pricing.get("plans", [])
|
||||
if not plans:
|
||||
return ""
|
||||
cards = "\n".join(f"""
|
||||
<div class="pricing-card {'featured' if p.get('featured') else ''}">
|
||||
<div class="pricing-name">{escape(p.get('name', ''))}</div>
|
||||
<div class="pricing-price">${escape(str(p.get('price', '0')))}<span>/mo</span></div>
|
||||
<p>{escape(p.get('description', ''))}</p>
|
||||
<ul class="pricing-features">
|
||||
{"".join(f'<li>{escape(f)}</li>' for f in p.get('features', []))}
|
||||
</ul>
|
||||
<a href="{escape(p.get('cta_url', '#'))}" class="btn-primary">{escape(p.get('cta_text', 'Choose Plan'))}</a>
|
||||
</div>""" for p in plans)
|
||||
return f"""
|
||||
<section class="pricing"><div class="container">
|
||||
<h2 class="section-title">{title}</h2>
|
||||
<div class="pricing-grid">{cards}</div>
|
||||
</div></section>"""
|
||||
|
||||
|
||||
def render_cta(cta: Dict[str, Any]) -> str:
|
||||
return f"""
|
||||
<section class="cta-section"><div class="container">
|
||||
<h2>{escape(cta.get('headline', 'Ready to get started?'))}</h2>
|
||||
<p>{escape(cta.get('subheadline', ''))}</p>
|
||||
<a href="{escape(cta.get('url', '#'))}" class="btn-white">{escape(cta.get('text', 'Start Free Trial'))}</a>
|
||||
</div></section>"""
|
||||
|
||||
|
||||
def generate_html(config: Dict[str, Any]) -> str:
|
||||
"""Generate complete HTML landing page."""
|
||||
title = escape(config.get("title", "Landing Page"))
|
||||
css = generate_css(config)
|
||||
sections = []
|
||||
sections.append(render_nav(config))
|
||||
if config.get("hero"):
|
||||
sections.append(render_hero(config["hero"]))
|
||||
if config.get("features"):
|
||||
sections.append(render_features(config["features"]))
|
||||
if config.get("testimonials"):
|
||||
sections.append(render_testimonials(config["testimonials"]))
|
||||
if config.get("pricing"):
|
||||
sections.append(render_pricing(config["pricing"]))
|
||||
if config.get("cta"):
|
||||
sections.append(render_cta(config["cta"]))
|
||||
sections.append(f"""
|
||||
<footer><div class="container">
|
||||
<p>{escape(config.get('footer_text', f'{datetime.now().year} {config.get("brand", "Company")}. All rights reserved.'))}</p>
|
||||
</div></footer>""")
|
||||
return f"""<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{title}</title>
|
||||
<meta name="description" content="{escape(config.get('meta_description', ''))}">
|
||||
<style>{css}</style>
|
||||
</head>
|
||||
<body>
|
||||
{"".join(sections)}
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate landing pages as HTML or Next.js TSX with Tailwind CSS"
|
||||
)
|
||||
parser.add_argument("input", help="Path to page config JSON")
|
||||
parser.add_argument(
|
||||
"--format", choices=["html", "tsx", "json"], default="tsx",
|
||||
help="Output format: tsx (Next.js + Tailwind), html (standalone), json (metadata)"
|
||||
)
|
||||
parser.add_argument("--output", type=str, default=None, help="Output file path")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.input) as f:
|
||||
config = json.load(f)
|
||||
|
||||
if args.format == "json":
|
||||
output = json.dumps({
|
||||
"generated_at": datetime.now().isoformat(),
|
||||
"config": config,
|
||||
"formats_available": ["html", "tsx"],
|
||||
"sections": [k for k in ["nav", "hero", "features", "testimonials", "pricing", "cta", "footer"]
|
||||
if config.get(k) or k in ("nav", "footer")]
|
||||
}, indent=2)
|
||||
elif args.format == "tsx":
|
||||
output = generate_tsx(config)
|
||||
else:
|
||||
output = generate_html(config)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
f.write(output)
|
||||
print(f"Landing page written to {args.output}")
|
||||
else:
|
||||
print(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user