Landing — Premium HTML Landing Page Generator¶
Distinct from
product-team/skills/landing-page-generator/. That skill outputs Next.js TSX components optimized for conversion / lead-gen. THIS skill outputs a single self-contained.htmlfile optimized for premium visual experience with GSAP animations. Pick by use case.
Generate a polished, self-contained .html landing page from a text prompt or brief. The output is ONE HTML file: all CSS inline in <style>, all JS inline in <script>, only external dependencies being Google Fonts + GSAP via CDN. The page is visually distinctive, animated, and production-quality.
Invocation Triggers¶
- "create a landing page"
- "build a landing page"
- "make a landing page for X"
- "I need a web page for Y"
- "promotional page"
- "product page"
- "one-pager"
- "web presence"
- "sales page"
- "landing for X"
Delivery Mode¶
In Claude Code CLI, write the file to disk at the specified path. In Claude.ai web, create an HTML artifact with the same content.
Phase 0: Grill-Me Intake (4 forcing questions, one at a time)¶
Dependency-ordered. Each question carries explicit "why I'm asking". Stop condition: max 4.
Q1 (root) — Product / Service¶
What's the product or service? Give me the name + a 1–2 sentence elevator pitch — what does it do, and who's it for?
Why I'm asking: The headline, subtext, and feature copy all derive from this. "App for productivity" produces generic boilerplate; "Async standup tool for remote engineering teams who hate Zoom" produces a landing page that converts.
Refuse mush. If user gives just a name with no pitch, push back once: "What does it do? Who's it for?" If still no pitch after push-back, deliver with explicit "generic positioning" caveat.
Q2 (depends on Q1) — Audience Register¶
Who's the audience? Pick one:
- Technical buyers (engineers, ops, security)
- Business buyers (PMs, execs, ops leaders)
- Consumers (general public, hobbyists)
- Internal (employees, partners — not for public sale)
Why I'm asking: Audience dictates copy register, jargon level, social-proof choices, and CTA framing. Technical buyers want specifics; consumers want benefits; internal pages can skip persuasion.
Forcing choice.
Q3 (always) — Brand Overrides¶
Brand colors / fonts to override the default (dark navy + teal + Inter)? Provide as: primary HEX, accent HEX, optional bg HEX. Or say "default" if you want the polished default.
Why I'm asking: The default is intentionally beautiful, but matching your brand makes the page feel native to your existing site. Even just a primary color override goes a long way.
Accept "default" or partial overrides (e.g., just primary). If only primary provided, derive accent algorithmically (lighten / darken).
Q4 (depends on Q1) — Tone¶
Tone — pick one:
- Professional — confident, restrained, B2B-friendly
- Playful — warm, light, occasional humor
- Authoritative — expert, data-forward, trust-building
- Minimal — terse, design-led, low copy density
Why I'm asking: Tone affects every sentence — headlines, microcopy, button text, closing copy. Picking upfront prevents tonal whiplash across sections.
Forcing choice. Recommended default: professional if Q2 = technical/business; playful if Q2 = consumer; minimal if the product is design-led.
Stop condition: After Q4, commit and generate. No follow-up questions during generation.
Content Extraction (with Fallback Strategy)¶
From Q1's elevator pitch, derive: - Hero headline — punchy version of "what it does" (8–12 words) - Hero subtext — version of "who it's for + payoff" (1–2 sentences) - 3–6 feature bullets — distilled from pitch + audience (Q2) + tone (Q4) - CTA text — action-oriented, matches tone - Closing copy — short, emotive, matches tone
Fallback when input is sparse: invent compelling content from product-name semantics + audience register. Flag inferred content with a comment in the HTML source (<!-- inferred: ... -->). Don't stall waiting for more input.
Brand System Specification¶
Default Color Palette (Dark Navy + Teal)¶
:root {
--navy: #0A1628;
--navy-mid: #0D1F38;
--teal: #00D4AA;
--teal-glow: rgba(0, 212, 170, 0.12);
--amber: #F5A623;
--off-white: #F7F7F2;
--text-muted: rgba(247, 247, 242, 0.68);
--card-bg: rgba(0, 212, 170, 0.06);
--card-border:rgba(0, 212, 170, 0.15);
}
Override Pattern¶
When Q3 provides custom brand values, the skill substitutes them into the :root block:
Brand override:
- primary: #FF6B35 → --navy / hero bg
- accent: #2EC4B6 → --teal / CTA / highlights
- bg: #011627 → --navy-mid / section bg
- text: #FDFFFC → --off-white
If only primary provided, derive accent algorithmically (lighten 15% for accent; darken 8% for navy-mid; convert to rgba at 0.12 alpha for glow). Use scripts/brand_palette_validator.py for the deterministic derivation.
See references/brand_system_design.md for color theory + WCAG + algorithmic palette derivation canon.
Typography¶
- Font family: Inter (via Google Fonts)
- Weight scale: 400 (body), 500 (eyebrow), 600 (links), 700 (subtitle), 800 (H1 + H2)
- Size scale:
- Hero H1: 68–82px
- Section H2: 52–62px
- Card titles: 22px
- Body: 17–19px
- Eyebrow: 13px (uppercase, letter-spaced)
- CTA button: 18px (500 weight)
Components (Must Specify CSS)¶
.btn-primary— CTA button with hover state (lift + brightness).feature-card— card with hover lift (translateY(-6px) + border-brighten).eyebrow— letter-spaced (0.2em) uppercase category label
Section 1: Hero¶
min-height: 100vh, flex-centered content- Optional eyebrow label above H1
- H1 (68–82px, 800 weight)
- Subtitle (17–19px, 1–2 sentences)
- CTA button (.btn-primary)
- Scroll-down indicator (animated chevron, CSS bounce)
- Depth layers (mouse parallax):
.hero-shapes-back— large blurred circles, absolute-positioned, low opacity.hero-shapes-mid— smaller shapes, sharper edges, higher opacity- Content layer (H1 + subtitle) — moves subtly in same direction as mouse
Section 2: Features¶
- 3 columns default (
repeat(3, 1fr)grid) - Responsive:
- 2 columns at 900px breakpoint
- 1 column at 580px breakpoint
- Each card:
- SVG icon (28px, stroke=var(--teal), no fill)
- Title (22px, 700 weight)
- Description (15–16px, --text-muted)
- Hover state:
transform: translateY(-6px)border-color: var(--teal)(brighten from --card-border)transition: 0.3s ease
Section 3: Closing CTA¶
- Full-width,
background: var(--navy-mid) padding: 120px 24px, text-align: center- Large closing headline (52–62px, 800 weight)
- Short subtext (--text-muted, 1–2 sentences)
- CTA button with ambient radial-gradient glow behind it:
Animation Patterns¶
See references/gsap_animation_patterns.md for the canon. Five patterns required:
1. Hero Entrance (GSAP timeline)¶
// MUST use gsap.set() FIRST to prevent FOUC
gsap.set([".eyebrow", ".hero h1", ".hero .subtitle", ".btn-primary", ".scroll-down"], {
opacity: 0,
y: 30
});
const tl = gsap.timeline({ defaults: { ease: "power3.out" } });
tl.to(".eyebrow", { opacity: 1, y: 0, duration: 0.6 })
.to(".hero h1", { opacity: 1, y: 0, duration: 0.8 }, "-=0.3")
.to(".hero .subtitle", { opacity: 1, y: 0, duration: 0.6 }, "-=0.5")
.to(".btn-primary", { opacity: 1, y: 0, duration: 0.5 }, "-=0.3")
.to(".scroll-down", { opacity: 1, y: 0, duration: 0.4 }, "-=0.2");
2. Mouse Parallax¶
const hero = document.querySelector(".hero");
hero.addEventListener("mousemove", (e) => {
const x = (e.clientX / window.innerWidth - 0.5) * 2;
const y = (e.clientY / window.innerHeight - 0.5) * 2;
gsap.to(".hero-shapes-back", { x: x * 45, y: y * 22, duration: 0.8 });
gsap.to(".hero-shapes-mid", { x: x * 22, y: y * 11, duration: 0.8 });
gsap.to(".hero .container", { x: x * 8, y: y * 5, duration: 0.8 });
});
3. Scroll-Triggered Feature Cards¶
gsap.set(".feature-card", { opacity: 0, y: 55, rotateX: 18 });
ScrollTrigger.batch(".feature-card", {
start: "top 80%",
onEnter: batch => gsap.to(batch, {
opacity: 1, y: 0, rotateX: 0,
duration: 0.8,
stagger: 0.11,
ease: "power2.out"
})
});
4. Floating Decorative Shapes (CSS keyframes — NOT GSAP)¶
CSS handles ambient continuous motion (smoother, cheaper than GSAP for indefinite animations):
@keyframes floatA {
0%, 100% { transform: translate(0, 0) rotate(0deg); }
50% { transform: translate(20px, -30px) rotate(8deg); }
}
@keyframes floatB { /* different duration + rotation */ }
@keyframes floatC { /* different duration + rotation */ }
.hero-shapes-back .shape-a { animation: floatA 12s ease-in-out infinite; }
5. Scroll Indicator (CSS bounce)¶
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(8px); }
}
.scroll-down { animation: bounce 2s ease-in-out infinite; }
Required CDN Dependencies¶
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
NO other external CSS or JS files. All custom CSS in <style>, all custom JS in <script> blocks within the same HTML file.
See references/single_file_html_discipline.md for the inline-only rationale.
Layout Rules¶
- Container max-width: 1200px, centered
- Section padding:
120px 24px(vertical 120, horizontal 24, scales down on mobile) - Responsive breakpoints:
- 900px → features grid 3-col → 2-col
- 580px → all grids → 1-col; H1 scales down to ~52px
- Viewport meta:
<meta name="viewport" content="width=device-width, initial-scale=1">
Output Spec¶
- Path:
${OUTPUT_DIR}/<product-name-kebab>.html - Default
${OUTPUT_DIR}:./landing-pages/ - Filename: lowercase kebab-case from product name ("Quill AI" →
quill-ai.html). Usescripts/kebab_slug_generator.pyfor deterministic slug generation + duplicate detection. - Self-contained: all CSS in
<style>, all JS in<script>, only Google Fonts + GSAP CDN external.
Validation (Post-Generation)¶
Run scripts/html_validator.py --file ${OUTPUT_DIR}/<slug>.html after generation. Checks:
- All 3 required sections present (
.hero,.features,.closing-cta) - CDN deps present (Inter + GSAP + ScrollTrigger)
gsap.set()initial states precede anygsap.timelineorgsap.to(FOUC prevention)- Responsive breakpoints at 900px + 580px
- No external
<link rel="stylesheet">other than Google Fonts - No external
<script src=>other than GSAP CDN <meta name="viewport">present- All animated elements have initial-state declarations
Error Handling¶
| Situation | Behavior |
|---|---|
| Input is just a name with no context | Invent compelling content from name semantics + audience register; flag as <!-- inferred --> in HTML source |
| Input file is large or PDF | Read fully before generating; don't truncate |
| Brand colors insufficient (only 1 HEX provided) | Use as primary; derive secondary/accent algorithmically (lighten/darken via brand_palette_validator.py) |
| Features count not specified | Default to 4 |
| Output dir doesn't exist | Create it |
| Existing file at output path | Append timestamp suffix or ask user (kebab_slug_generator.py flags duplicates) |
| html_validator returns FAIL | Regenerate ONLY the failing sections in one targeted pass; do NOT abandon the file |
Portability¶
- Claude Code CLI: Native — writes HTML file directly to filesystem.
- Claude.ai web: Native — produces HTML as an artifact instead of file.
Tooling¶
| Script | Role |
|---|---|
scripts/brand_palette_validator.py |
Validates HEX format, checks WCAG AA contrast, generates derived palette from primary (algorithmic lighten/darken). |
scripts/kebab_slug_generator.py |
Product name → kebab-case filename + duplicate detection in output dir. |
scripts/html_validator.py |
Post-generation structural check: 3 sections, CDN deps, gsap.set() initial states, responsive breakpoints, no external files. |
References¶
references/brand_system_design.md— color theory + WCAG + algorithmic palette derivation (7+ sources)references/gsap_animation_patterns.md— entrance timeline + ScrollTrigger reveals + mouse parallax + CSS floats + scroll indicator (7+ sources)references/single_file_html_discipline.md— why inline + CDN-only externals + accessibility minimums + no-build rationale (7+ sources)
Anti-Patterns To Reject¶
- Hardcoded absolute paths in output directory
- Single brand palette without override documentation
- Outlining before writing — write in one pass
- External CSS or JS files (must be inline; only Google Fonts + GSAP CDN allowed)
- Skipping
gsap.set()initial states (causes FOUC) - More than 6 features in default grid (becomes unscannable)
- Brand-specific content references in the skill itself
Version: 1.0.0
Source spec: megaprompts/04-landing-megaprompt.md
Build pattern: Path B (direct conversion). Distinct from product-team/skills/landing-page-generator/.