Cairn
The product tour that scrolls itself.
Field notes
A complete SaaS landing page where the app window pins in place and changes screens as you scroll: KPIs count up, a funnel grows, a cohort grid fills in, an alert fires. Every chart is hand-built HTML and SVG. No chart library, no images, no WebGL.
- Scroll-driven
- SVG charts
- SaaS
- Dark theme
Cairn is a full landing page for a product analytics tool: header, hero with a customer strip, a pinned product tour, three-tier pricing with a monthly and yearly toggle, and a closing call to action. The tour is the centrepiece. While you scroll through it, the app window stays on screen and walks through four views (Overview, Funnels, Cohorts, Alerts), each building itself as it arrives, with the sidebar highlight sliding from row to row and the caption beside it changing.
Everything in the window is plain HTML and SVG, drawn from seeded data so it looks the same on every load. The window is laid out at one fixed design size and scaled to fit, so it keeps its proportions from a phone to a wide monitor; on a phone the sidebar drops away so the screen itself gets the width. Anyone who prefers reduced motion sees each screen already built, with plain cross-fades.
How it's built
- One number runs the whole tour: a Framer Motion value from 0 to 4, where screen i is on between i and i + 1. The section's scroll progress feeds it through a spring, and every caption, chart, counter, and the sidebar highlight is a pure function of it, so scrolling back up plays everything in reverse.
- Screens build in stages, not on timers: each element maps its own slice of its screen's progress (say, 0.12 to 0.55) to a path length, a scaleY, or an opacity, so the line chart draws, the funnel bars grow one after another, and the cohort cells light up along the diagonal as you scroll.
- Hand-offs without clutter: the old screen fades out before the new one fades in, so two sets of text never overlap, while the sidebar highlight uses a symmetric cross-fade and glides straight from one row to the next.
- The app window is a fixed 960 × 600 layout scaled with one CSS transform, measured by a ResizeObserver. Below 560 px it switches to a 560-wide layout without the sidebar and hangs from the top of its box instead of centring.
Inside the prompt
Every file below is written out in full, so your agent creates the project exactly — no guessing, no missing pieces.
cairn-tour/
├─ .gitignore
├─ index.html
├─ package.json
├─ tsconfig.json
├─ vite.config.ts
└─ src/
├─ App.tsx
├─ index.css
├─ main.tsx
├─ vite-env.d.ts
├─ components/
│ ├─ AppFrame.tsx
│ ├─ Hero.tsx
│ ├─ Logo.tsx
│ ├─ Pricing.tsx
│ ├─ Tour.tsx
│ └─ screens/
│ ├─ Alerts.tsx
│ ├─ Cohorts.tsx
│ ├─ Funnel.tsx
│ └─ Overview.tsx
└─ lib/
├─ data.ts
└─ loop.tsRecut it
- Rewrite the four tour captions and sidebar names
src/lib/data.ts → FEATURES - Swap in your own KPIs, funnel steps, or cohort weeks
src/lib/data.ts → KPIS / FUNNEL / COHORT_WEEKS - Change the plans, prices, and features
src/lib/data.ts → PLANS - Replace a screen of the app window with your own UI
src/components/AppFrame.tsx → SCREENS - Make the tour longer or shorter to scroll through
src/components/Tour.tsx → Tour() - Recolour the page (paper, ink, and the signal orange)
src/index.css → @theme - Change the headline and the customer strip (the name Cairn also sits in index.html, AppFrame, Pricing's Footer, and FEATURES)
src/components/Hero.tsx → Hero() / CUSTOMERS
More in Landing Page
All landing page templates →
Strata
Landing PageScroll and the camera sinks down a shaft of hexagonal basalt columns, past four chapters, to a molten core. Instanced Three.js prisms, one small lava shader, and a scroll spring: no models, no textures.

Meridian
Landing PageA dark hero for an API or infra product: a dotted globe drawn from real coastlines, with traffic arcs firing between cities and landing in a ping. Drag to spin it. No models, no textures, no map requests.

Relay
Landing PageA landing page for an AI agent that isn't purple: ink on warm paper, one orange. The hero is a live tool-call graph where every call leaves a wet-ink trail, tools answer when you click them, and scrolling folds the whole run into a timeline. One raw WebGL2 shader, no three.js.