# Marketing Pages

> Use when building a landing, pricing, or launch page: hero structure, section rhythm, social proof, scroll behavior, CTA hierarchy, and plan comparison tables.

- Skill: `agentsorg/marketing-pages` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add agentsorg/marketing-pages`
- Raw SKILL.md: https://api.skillmd.com/api/skills/agentsorg/marketing-pages/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- Author: agentsorg (https://skillmd.com/u/agentsorg)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/agentsorg/marketing-pages

---



# Marketing Pages

A marketing page is read once by someone who did not ask to be there, which inverts most product-UI defaults: the animation budget goes up, the information density goes down, and the page proves one claim instead of exposing every capability. Default to a short page — one claim, one primary CTA repeated verbatim, evidence beside each promise rather than pooled at the bottom. The larger motion budget is an allowance, not a licence: anything that moves without the visitor causing it reads as the page performing at them while they are trying to decide. This skill builds one page inside a system it does not own — if the open question is which accent, typeface, illustration voice, or tone the page speaks in, that is `brand-identity`; here those are already decided and only arranged.

**Detect the stack before writing a line.** Read how the project already builds pages — framework and rendering mode (static export, ISR, server components), styling system (Tailwind, CSS Modules, custom properties, CSS-in-JS), the existing section and container components, and the animation library if one is present. Write the page in that system. A landing page is the most common place a parallel styling layer or a second animation library gets bolted on, and it is never justified: if the project ships CSS transitions, the hero does not import a motion library for one fade.

## Quick Reference

| Topic | File |
| --- | --- |
| Building or ordering a specific section — hero, logo wall, feature block, demo, testimonial, FAQ, final CTA, or an attached blog/changelog/docs surface. Open it when you know the page needs a section but not what belongs inside it. | [section-catalog.md](references/section-catalog.md) |
| Anything with a price on it: tier count, highlighted plan, annual/monthly toggle, feature comparison table, or a page that has to answer objections. Open it whenever the page shows money. | [pricing-and-conversion.md](references/pricing-and-conversion.md) |

## Core Principles

1. **Motion still has to map to user input.** No scroll-triggered fade-ups, no scroll hijacking, no auto-advancing carousel, no parallax that is not `1:1` with scroll position. Motion the visitor did not cause reads as a performance, and a self-advancing carousel moves the thing being read. *Exception:* a reveal scrubbed by scroll position — reversible, running backwards when you scroll up — is direct manipulation, not a trigger, and is allowed.
2. **Spend the extra animation budget on one sequence, not on every section.** Product UI stays under `300ms` (Emil Kowalski's duration table, whose only "can be longer" row is marketing/explanatory). One hero or explanatory sequence may run out to Benji Taylor's published `--duration-swift: 800ms / cubic-bezier(.175,.885,.32,1.275)`. Note the live disagreement: Benji's `snappy` and `swift` curves overshoot past 1, Emil's three all settle — pick one family for the project and never mix them (see `motion/references/techniques.md`). *Exception:* anything clickable stays under the product ceiling. A CTA that takes 800ms to acknowledge a press is broken on any page type.
3. **The intro animation plays once per session.** Gate it on `sessionStorage`, not `localStorage` — a genuinely new visit should see it, an internal navigation should not. The page animates in once and then stops moving. *Exception:* under `prefers-reduced-motion: reduce`, skip the intro entirely, first visit included; reduced motion means gentler, not delayed.
4. **The hero says what it is, who it is for, and shows it working.** One claim sentence, one qualifying sentence, one primary CTA, one secondary escape hatch (docs, demo, pricing), and one piece of evidence that is the actual product — not an abstract gradient blob. Cropping, framing, and loading of that evidence belong to `imagery`. *Exception:* a pre-launch or waitlist page with nothing to show — there the email field is the hero.
5. **One primary CTA, same verb every time it appears.** "Get started" in the nav and "Try it free" in the footer read as two different offers. Ship `1` visually primary action per viewport, repeats identical in wording and destination. CTAs also read auth state: logged out gets `Get Started`, logged in gets `Go to Dashboard` — telling a signed-in user to sign up is a dead end. *Exception:* the pricing page, where each tier card carries its own primary and the hierarchy moves to the highlighted tier.
6. **Proof goes next to the claim it proves.** A "trusted by" band at the bottom proves nothing about a feature described three screens above it. Attach the logo, quote, or number to the section making the claim. *Exception:* a first-viewport logo wall earns its place only when the logos are recognizable to this specific reader; otherwise it reads as filler and belongs below the first feature section.
7. **Ship `5-7` sections and cut the eighth** (house default). Each extra section dilutes the one claim and pushes the closing CTA further away. A capability that cannot earn a section belongs in docs. *Exception:* developer tools, where a code block plus its output often needs one section per concept.
8. **Everything a crawler or screen reader needs is in the DOM at rest.** Hover-revealed nav submenus are visually hidden with `aria-expanded` on the trigger, never conditionally mounted — mount-on-hover markup does not exist for a crawler and cannot be reached by a keyboard. *Exception:* a menu large enough to bloat first paint may lazy-mount, but only behind a real `<button>`, with its links duplicated in the footer.
9. **Pre-render, preload, and lock the space.** Content pages are statically generated with revalidation (`export const revalidate = 3600`), never fetched at request time; fonts and the above-the-fold hero image are preloaded; headings get `text-wrap: balance` so no title strands a single word. *Exception:* per-visitor content (the auth-aware CTA, a live counter) hydrates on the client over pre-rendered markup — it does not make the page a request-time render.
10. **Decorative illustrations built from DOM elements need a name and no pointer surface.** Give them `role="img"` with an `aria-label`, plus `user-select: none` and `pointer-events: none`, or they get selected mid-drag and announced as a pile of empty divs. *Exception:* an interactive illustration keeps its pointer events, and then owes a keyboard path too.

## Smell / Fix

| Smell | Fix |
| --- | --- |
| Sections fade up as they enter the viewport | Delete the observer. Render them visible; motion that fires on scroll fires on every scroll direction and fights the reader |
| Hero carousel rotating through three value props | You have three claims and therefore no claim. Pick one, demote the rest to feature sections |
| Nav says "Get started", hero says "Start free trial", footer says "Sign up" | One verb, one destination, repeated verbatim |
| A wall of unfamiliar logos under a "Trusted by" caption | Replace with one named quote plus a number, or move it below the first feature section |
| `localStorage` guarding the intro animation | `sessionStorage` — the intro returns on a real new visit |
| Blog or changelog fetched at request time | Static generation plus revalidation; these change hourly at most |
| Webfont swaps in and the hero reflows | Preload the font file and declare a metric-matched fallback stack |
| Pricing table only readable by scrolling sideways on mobile | Reflow to one card per tier — see [pricing-and-conversion.md](references/pricing-and-conversion.md) |
| A motion library imported for one hero fade | Use the project's existing transitions; the library outweighs the effect |
| `<div>` with an `onClick` acting as the CTA | A real `<a>` or `<button>` — the CTA is the one element that must survive a crawler, a middle-click, and a keyboard |

## Output Format

When proposing or reviewing a page, emit a section manifest before any code — one row per section, in scroll order:

| # | Section | The one claim it makes | Evidence on screen | CTA |
| --- | --- | --- | --- | --- |
| 1 | Hero | Deploys in one command | 12-second product recording | primary |
| 2 | Logo wall | Four teams you recognize already use it | 6 marks, greyscale | none |
| 3 | Feature | Rollbacks are instant | Annotated screenshot of the rollback | inline link |

A row with no claim is a section to cut. A row whose evidence reads "illustration" is a claim with no proof. Two primary CTAs with different wording is a defect, not a variant.

## Checklist

- [ ] One claim, stated in the hero in one sentence, with the product visible
- [ ] `5-7` sections, each with a claim and its own evidence
- [ ] One primary CTA, identical wording and destination everywhere it repeats
- [ ] CTA copy and href switch on auth state
- [ ] No scroll-triggered reveals, scroll hijacking, non-`1:1` parallax, or auto-advancing carousel
- [ ] One curve family across the page; no clickable element animates over `300ms`
- [ ] Intro animation gated on `sessionStorage` and skipped under `prefers-reduced-motion`
- [ ] Statically generated with revalidation; fonts and hero image preloaded; no layout shift
- [ ] Nav submenu markup present in the DOM when closed, `aria-expanded` on the trigger
- [ ] `text-wrap: balance` on headings; code-built illustrations have `aria-label`, `user-select: none`, `pointer-events: none`
- [ ] Pricing, if present, reviewed against [pricing-and-conversion.md](references/pricing-and-conversion.md)

