| name | build-landing-page |
| description | Use when building or restructuring a landing page, marketing page, or any public conversion page — audits the page against a section grammar (hero, social proof, features-as-benefits, pricing, FAQ, final CTA), enforces one conversion goal per page, a semantic HTML skeleton, and a hero LCP/CLS budget. Framework-agnostic — works on built HTML from any stack. |
Build Landing Page
A landing page has one job. This skill names it, audits which sections serve it, adds
only what's missing, and holds the hero to a performance budget — because on a public
page LCP and CLS are ranking and revenue, not polish.
1. Gate — is this a public page?
Run the gate from ../_shared/page-types.md: must this page be findable and readable
without JS? If it's an authenticated app surface, stop — this skill doesn't apply; the
skills/frontend/ catalogue owns app screens.
2. Audit current state
Work from the built/served HTML (a URL or a file from dist//build//out/):
curl -s "$URL" -o /tmp/page.html
grep -ci "<h1" /tmp/page.html
grep -oE "<(header|main|section|footer|nav)\b" /tmp/page.html | sort | uniq -c
grep -oiE "<(img|picture|video)[^>]*" /tmp/page.html | head -5
Read the page and list: which grammar sections exist, what the h1 claims, how many
competing CTAs there are. Prerequisite check: if the main content isn't in the HTML
(view-source test, ../_shared/page-types.md), fix crawlability first —
../set-up-seo/SKILL.md.
3. Name the one job
Every landing page converts toward exactly one action: sign up, buy, book, subscribe,
download. Name it in one sentence ("this page exists to get a demo booked"). If nobody
can, that is the first finding — no section work matters until the job is named. Every
primary CTA on the page is that action; anything else is subordinate (footer, text
links).
4. Decide what to do
- No page yet → compose from the grammar (step 5), required sections first.
- Page exists, sections missing for its goal → add only those.
- All sections present → tighten against the grammar's must-haves (step 5) and the
budget (step 7). Re-running on a conforming page changes nothing.
5. Section grammar
Walk top to bottom; details and anti-patterns in ./section-grammar.md.
| Section | Its job | Must contain | Skip when |
|---|
| Hero | state value + action | h1 = outcome (not product name); one primary CTA above the fold; LCP visual on budget | never |
| Social proof | borrow trust | real logos / named quotes / specific numbers | nothing real yet — omit, don't fake |
| Problem → solution | mirror the pain | the problem in the visitor's words, then the turn | product self-evident (waitlist) |
| Features as benefits | capability → outcome | each item leads with what the visitor gets | single-feature page |
| Pricing | remove the price question | real numbers or honest "from …"; one recommended tier | no self-serve price |
| FAQ | answer created objections | 4–8 real questions, direct answers (feeds FAQPage schema) | nothing left open |
| Final CTA | catch the convinced scroller | one line of value + the same primary action; no new links | single-screen page |
6. Semantic skeleton
Landmarks and heading order are accessibility and machine-readability at once:
<header></header>
<main>
<section aria-labelledby="hero-h">
<h1 id="hero-h">Outcome the visitor gets</h1>
<p>One supporting line — the mechanism.</p>
<a class="cta" href="#signup">The one action</a>
</section>
<section aria-labelledby="proof-h"><h2 id="proof-h">…</h2>…</section>
</main>
<footer></footer>
Exactly one h1. Every section gets a real heading — screen readers and answer engines
navigate by them.
7. Hero performance budget
The hero owns LCP and most CLS risk. Targets: LCP ≤ 2.5 s, CLS ≤ 0.1 (mobile,
mid-tier device).
<img src="/hero.avif" alt="What the image shows" width="1200" height="800"
fetchpriority="high" decoding="async">
<link rel="preload" href="/fonts/heading.woff2" as="font" type="font/woff2" crossorigin>
@font-face { font-family: Heading; src: url(/fonts/heading.woff2) format('woff2');
font-display: swap; }
- Modern format (AVIF/WebP), sized for its largest rendered width — not the original.
Bare AVIF accepts a small no-support tail; wrap in
<picture> with WebP/JPEG
<source> fallbacks when the audience skews to older browsers.
width/height (or CSS aspect-ratio) on every image so nothing shifts.
- No carousel, no video, no JS-gated rendering in the hero. Scripts use
defer (or
type="module") and sit below the fold; a public page's JS budget starts at zero
(../_shared/page-types.md).
8. Verify
curl -s "$URL" | grep -ci "<h1"
curl -s "$URL" | grep -ci "fetchpriority"
View the page with JS disabled: full content, readable order. Load on a throttled
mobile profile: text visible immediately (no font flash of invisible text), nothing
shifts. Run Lighthouse on the deployed URL: LCP ≤ 2.5 s, CLS ≤ 0.1.
References
- ./section-grammar.md — per-section rules, anti-examples, when to deviate.
- ./stack-pointers.md — where hero/meta wiring lives in Next, Astro, Nuxt, Vite SPA.
- ../set-up-seo/SKILL.md — crawlability, metadata, structured data for this page.
- ../set-up-lead-capture/SKILL.md — when the one action is a form.
- ../../frontend/optimize-performance/SKILL.md — app-side CWV work (the other side of the priority inversion).