---
name: "Landing page (Vercel)"
repo: https://github.com/shipshitdev/skills
source: https://github.com/shipshitdev/skills/blob/HEAD/skills/landing-page-vercel/SKILL.md
license: not specified
install: "npx skills add shipshitdev/skills --skill landing-page-vercel"
page: https://100xlab.com/resources/agent-skills/shipshitdev-skills--landing-page-vercel--1a3458g
---

# Landing Page (Vercel)

Create a static landing page with:

## Contract

Inputs:

- Product name, tagline, audience, offer, and CTA
- Destination directory
- Form provider and analytics preference

Outputs:

- Static landing page files
- Form/analytics setup notes
- Deployment instructions

Creates/Modifies:

- Local landing page files and Vercel config
- Does not deploy production unless explicitly requested

External Side Effects:

- None during scaffolding
- May deploy to Vercel only after explicit deploy request

Confirmation Required:

- Before using external form/analytics identifiers
- Before running Vercel deploy commands
- Before overwriting an existing landing page directory

Delegates To:

- `project-init-orchestrator` / `npx @shipshitdev/v0` for full Shipshit.dev product repos
- `frontend-design` for custom visual design
- `deployment-composer` or `deploy` for deployment

- **Structure:** Semantic HTML5 + Modern CSS + Vanilla JS
- **Form:** Working email capture (Formspree or custom endpoint)
- **Analytics:** Plausible/Fathom ready
- **Design:** Responsive, accessible, performant
- **Deploy:** One-click Vercel deployment

## What Makes This Different

This skill generates **working landing pages**, not empty templates:

- Real email capture form that actually submits
- Analytics integration ready to activate
- Responsive design tested on mobile
- Accessibility basics (WCAG 2.1 AA)
- Content from your PRD brief

---

## Workflow

### Phase 1: PRD Brief Intake

Ask the user for product details, then extract and confirm: product name, tagline, hero headline/subheadline/CTA, 3-5 features with descriptions, CTA type (Waitlist/Sign Up/Demo Request/Contact), and social proof preference (Testimonials/Logos/Stats/None). See `references/full-guide.md` (§ PRD Brief Intake Template) for the confirmation message format.

### Phase 2: Content Generation

Generate complete landing page content across these sections:

1. **Hero** - Headline, subheadline, CTA button, optional hero image
2. **Features** - 3-5 feature cards with icons
3. **How It Works** - 3-step process (optional)
4. **Social Proof** - Testimonials or logos (optional)
5. **FAQ** - 4-6 common questions (optional)
6. **CTA** - Final call to action with form
7. **Footer** - Links, copyright, social icons

### Phase 3: Form Integration

Choose an email capture option:

1. **Formspree (Recommended - Free tier)** - no backend needed, instant setup, email notifications
2. **Custom Endpoint** - your own API, full control, requires backend
3. **Waitlist Service** - Waitlist.email, Loops.so, ConvertKit

### Phase 4: Quality Verification

Verify before handoff:

- HTML validates (W3C)
- Responsive on mobile
- Form submits successfully
- Analytics placeholders present
- Lighthouse score 90+

---

## Usage

```bash
# Create landing page with PRD
python3 scripts/scaffold.py \
  --out ./my-landing-page \
  --name "ProductName" \
  --tagline "Your compelling value proposition" \
  --features "Feature1,Feature2,Feature3"

# Interactive mode
python3 scripts/scaffold.py --out ./my-landing-page --interactive
```

Generates `index.html`, `styles.css`, `script.js`, `data.json`, `vercel.json`, and an `assets/` directory. See `references/full-guide.md` (§ Generated Structure) for the full file tree.

---

## Key Patterns

Form submission handles loading/success/error states via `fetch` against the form's action URL; content lives in `data.json` (hero, features, faq) so copy edits never touch markup. See `references/full-guide.md` (§ Form Handling (JavaScript), § Data Structure (data.json)) for the full implementations.

---

## Form Integration Guide

**Formspree (Recommended):** create a free account at [formspree.io](https://formspree.io), create a form, and replace `YOUR_FORM_ID` in the HTML with the generated form ID.

**Custom Endpoint:** post `{ email }` as JSON to a self-hosted API and handle the response. See `references/full-guide.md` (§ Option 2: Custom Endpoint) for the full snippet.

---

## Analytics Setup

Add one script tag to the page head: Plausible (`data-domain` + `plausible.io/js/script.js`) for privacy-friendly analytics, or Fathom (`cdn.usefathom.com/script.js` + `data-site`). See `references/full-guide.md` (§ Analytics Setup) for both tags.

---

## Deployment

```bash
cd my-landing-page
bunx vercel          # Deploy (no global install needed)
bunx vercel --prod   # Production deploy
```

`vercel.json` maps all static assets through `@vercel/static`, routes everything to `index.html`, and sets baseline security headers (`X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`). See `references/full-guide.md` (§ Vercel Configuration (vercel.json)) for the full config.

---

## CSS Variables

Theme tokens (`--color-primary`, `--color-bg`, `--font-sans`, `--spacing-*`, `--max-width`) drive the whole stylesheet — see `references/full-guide.md` (§ CSS Variables) for the full `:root` block.

---

## Accessibility Checklist

- [x] Semantic HTML structure
- [x] Proper heading hierarchy (h1 → h2 → h3)
- [x] Alt text for images
- [x] Focus states for interactive elements
- [x] Color contrast ratio 4.5:1 minimum
- [x] Form labels and error messages
- [x] Skip link for keyboard navigation
- [x] Responsive text sizing (no fixed px for body text)

---

## Performance Checklist

- [x] No external CSS frameworks
- [x] Minimal JavaScript
- [x] Optimized images (WebP with fallback)
- [x] System fonts (no web font loading)
- [x] Lazy loading for below-fold images
- [x] Preconnect for external resources

---

## References

- `scripts/scaffold.py` - Generation script (templates are inline)
- `references/full-guide.md` - Full PRD template, generated file tree, form/analytics/deploy config dumps, and CSS variable set

---

## Supporting file: plugin.json

{
  "name": "landing-page-vercel",
  "version": "1.0.1",
  "description": "Scaffold a production-ready static landing page with working email capture form, analytics, and resp",
  "author": {
    "name": "Ship Shit Dev",
    "email": "hello@shipshit.dev",
    "url": "https://shipshit.dev"
  },
  "license": "MIT",
  "skills": "."
}

---

## Supporting file: references/full-guide.md

# Landing Page (Vercel) — Full Guide

Full templates, config dumps, and integration snippets for the landing-page-vercel skill. See `SKILL.md` for the Contract, workflow phases, and quality checklists.

## PRD Brief Intake Template

```
I'll help you create a landing page. Based on your description:

**Product:** [Name]
**Tagline:** [One-line value proposition]

**Hero Section:**
- Headline: [Main headline]
- Subheadline: [Supporting text]
- CTA: [Button text]

**Features:** (3-5)
1. [Feature 1]: [Description]
2. [Feature 2]: [Description]
3. [Feature 3]: [Description]

**CTA Type:** [Waitlist / Sign Up / Demo Request / Contact]

**Social Proof:** [Testimonials / Logos / Stats / None]

Is this correct? Any adjustments?
```

## Generated Structure

```
my-landing-page/
├── index.html           # Main landing page
├── styles.css           # All styles (no framework)
├── script.js            # Form handling + interactions
├── data.json            # Content data (easy to edit)
├── vercel.json          # Vercel configuration
├── assets/
│   ├── favicon.ico
│   └── og-image.png     # Social sharing image
└── README.md            # Deployment instructions
```

## Form Handling (JavaScript)

```javascript
document.addEventListener('DOMContentLoaded', () => {
  const form = document.getElementById('signup-form');
  const button = form.querySelector('button[type="submit"]');
  const messageEl = document.getElementById('form-message');

  form.addEventListener('submit', async (e) => {
    e.preventDefault();

    const originalText = button.textContent;

    try {
      button.textContent = 'Submitting...';
      button.disabled = true;

      const response = await fetch(form.action, {
        method: 'POST',
        body: new FormData(form),
        headers: { 'Accept': 'application/json' }
      });

      if (response.ok) {
        // Hide form and show success message
        form.style.display = 'none';
        messageEl.textContent = 'Thanks! We will be in touch.';
        messageEl.classList.add('success');
      } else {
        throw new Error('Form submission failed');
      }
    } catch (error) {
      button.textContent = originalText;
      button.disabled = false;
      messageEl.textContent = 'Something went wrong. Please try again.';
      messageEl.classList.add('error');
    }
  });
});
```

## Data Structure (data.json)

```json
{
  "name": "ProductName",
  "tagline": "Your compelling value proposition",
  "hero": {
    "headline": "Build something amazing",
    "subheadline": "The easiest way to create, launch, and grow your product.",
    "cta": "Join the Waitlist"
  },
  "features": [
    {
      "icon": "zap",
      "title": "Lightning Fast",
      "description": "Built for speed from the ground up."
    },
    {
      "icon": "shield",
      "title": "Secure by Default",
      "description": "Enterprise-grade security included."
    },
    {
      "icon": "sparkles",
      "title": "AI-Powered",
      "description": "Smart features that learn from you."
    }
  ],
  "faq": [
    {
      "question": "When will you launch?",
      "answer": "We're aiming for Q1 2026. Join the waitlist to be first to know."
    }
  ]
}
```

## Form Integration Guide

### Option 1: Formspree (Recommended)

1. Go to [formspree.io](https://formspree.io)
2. Create a free account
3. Create a new form
4. Copy your form ID
5. Replace `YOUR_FORM_ID` in the HTML

### Option 2: Custom Endpoint

```javascript
// In script.js, update the form action
const API_URL = 'https://your-api.com/api/waitlist';

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const email = form.querySelector('input[name="email"]').value;

  const response = await fetch(API_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email })
  });
  // Handle response...
});
```

## Analytics Setup

### Plausible (Privacy-friendly)

```html
<!-- Add to head section -->
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.js"></script>
```

### Fathom

```html
<!-- Add to head section -->
<script src="https://cdn.usefathom.com/script.js" data-site="YOUR_SITE_ID" defer></script>
```

## Deployment

### Vercel (One-click)

```bash
cd my-landing-page
bunx vercel          # Deploy (no global install needed)
bunx vercel --prod   # Production deploy
```

### Vercel Configuration (vercel.json)

```json
{
  "version": 2,
  "builds": [
    { "src": "*.html", "use": "@vercel/static" },
    { "src": "*.css", "use": "@vercel/static" },
    { "src": "*.js", "use": "@vercel/static" },
    { "src": "assets/**", "use": "@vercel/static" }
  ],
  "routes": [
    { "src": "/(.*)", "dest": "/index.html" }
  ],
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "X-Frame-Options", "value": "DENY" },
        { "key": "X-XSS-Protection", "value": "1; mode=block" }
      ]
    }
  ]
}
```

## CSS Variables

```css
:root {
  --color-primary: #3b82f6;
  --color-primary-dark: #2563eb;
  --color-bg: #0f172a;
  --color-bg-secondary: #1e293b;
  --color-text: #f8fafc;
  --color-text-muted: #94a3b8;
  --font-sans: system-ui, -apple-system, sans-serif;
  --max-width: 1200px;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 2rem;
  --spacing-xl: 4rem;
}
```
