Skip to content
LandingPagePrompts
Landing PageMiniMaxAI/MiniMax-M3verified 23/23partial cross-check

Scroll Atlas

Pinned scroll-scrubbed cinematic narrative with a progress rail

The exact prompt

DESIGN BRIEF — "Scroll Atlas"

Build a one-page scroll-driven narrative for a fictional cartography studio called
"Atlas Nine". The page must feel like scrubbing a film timeline: the user scrolls, and a
pinned stage performs choreographed, reversible motion. Nothing here should be a
"fade in on scroll" — every transform must be *driven* by scroll progress.

A. THE PINNED STAGE
   - Structure: a `<section class="stage">` that is 420vh tall, containing a
     `position: sticky; top: 0; height: 100vh` inner viewport. Do NOT use
     position: fixed. The sticky inner viewport holds the canvas, the type, and the rail.
   - Progress p = clamp(scrollTop_of_section / (sectionHeight - viewportHeight), 0, 1),
     computed in the rAF loop from a cached rect (recompute the rect only on resize and
     on a 250ms debounce of scroll — never call getBoundingClientRect inside rAF).
   - The stage is divided into 4 beats at p = 0.00, 0.25, 0.50, 0.75. Each beat has a
     title, a body line and a colour. Cross-fade beats with a 0.08-wide smoothstep
     window so there is never a hard cut.

B. THE VISUAL — a drawn world, not an image
   - A full-viewport <canvas> renders an abstract "atlas": a slowly rotating globe of
     latitude/longitude arcs (stroke rgba(255,255,255,.14), 1px) drawn with simple
     3D→2D projection maths you write yourself (no library). Only the front hemisphere
     is drawn; back-facing segments are skipped.
   - As p advances, the camera dollies in (radius 1.0 → 0.42), the globe tilts
     (tilt 0 → -0.6 rad), and a set of 12 "survey pins" (small filled circles with a
     1.5px halo) fade in one by one between p = 0.25 and 0.75, each with a thin arc
     connecting it to its predecessor — a route being drawn.
   - The globe stroke colour lerps between the four beat colours. Add a soft radial
     bloom behind the globe whose radius is driven by p.
   - Performance: precompute the sphere point set once (lat rings of 18, lon rings of 36)
     into typed arrays; per frame only rotate + project. Target 60fps at 1440x900.

C. THE RAIL
   - A vertical progress rail fixed to the right edge, 2px wide, 34vh tall, centred:
     track rgba(255,255,255,.12), fill a gradient that grows with p. Four 6px tick
     marks sit at 0/25/50/75% and the active tick grows to 10px and turns solid white.
     The rail is aria-hidden and purely decorative, but the *text* beats must be real
     content in the DOM.

D. TEXT
   - Beat 1 — "Nine degrees of nowhere" / "We began with a blank plate and a
     suspicion that the coastline was lying."
   - Beat 2 — "Survey, then argue" / "Twelve soundings, four disagreements, one
     corrected map."
   - Beat 3 — "The route writes itself" / "Each pin is a decision. The arc is the
     consequence."
   - Beat 4 — "Atlas Nine" / "Maps for places that are still deciding what they are."
   - Beats are absolutely stacked in the same grid cell; opacity and a 24px translateY
     are driven by p (no CSS transitions on them). Text must remain selectable and
     readable when p is 0.

E. FRAME
   - <header> a fixed, translucent, blurred top bar (background rgba(8,8,12,.55),
     backdrop-filter blur(12px), 1px bottom hairline) with the wordmark "ATLAS NINE",
     links "Work", "Studio", "Journal" and a bordered pill button "Start a map".
   - <main> holds the pinned stage, then one closing section (min-height 70vh) with an
     <h2> "Ready to redraw something?" and a large ghost button.
   - <footer> three columns: wordmark + one-line description, "Studio" link list,
     "Contact" link list; bottom row with the copyright.
   - Palette: base #08080c, ink #f2f2f5, muted rgba(242,242,245,.6), accent #c084fc,
     secondary #67e8f9. UI font: system stack. Display font: "Instrument Serif"
     (Google Fonts) for the h1/h2 only.

F. MOTION SAFETY
   - With prefers-reduced-motion: reduce, the section collapses to 100vh (via CSS), the
     sticky behaviour is removed, all four beats are laid out sequentially as static
     blocks, and the globe renders at a fixed camera with no rotation.

---

TECHNICAL CONTRACT (non-negotiable):

1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,
   no framework, no package install. It must open correctly by double-clicking the file
   and also over http://.
2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.
   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.
3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of
   photos. Every visual must be generated procedurally with CSS gradients, SVG, or
   <canvas>. This keeps the page free to host and impossible to break with a dead link.
4. Google Fonts is the ONLY permitted network request, and it must be declared with a
   <link rel="preconnect"> plus a system-font fallback stack so the page still looks
   intentional offline. If you prefer, skip it and use a pure system stack.
5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).
   No horizontal scrollbar at any of those widths. Nothing may overflow its container.
6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every
   interactive element is a <button> or <a> with an accessible name; visible :focus-visible
   ring; body text contrast ratio >= 4.5:1 against its background.
7. Motion safety: wrap all continuous motion in
   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or
   near-static state. The page must remain fully readable and usable with motion off.
8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single
   requestAnimationFrame loop, use transform/opacity for animation, and never allocate
   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.
9. Runtime telemetry hook (used by the automated verifier — implement it exactly):
   at the top of your script define
     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };
   then set `ready = true` after the first paint of your main effect, increment
   `frames` once per animation frame inside your rAF loop, and push the name of each
   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`
   handler that pushes messages into `errors`. Do not rename these fields.
10. Zero console output in the happy path. No console.log left behind. No uncaught
    exceptions, no failed network requests.

RESPONSE FORMAT:
Return ONLY the raw HTML inside a single ```html fenced code block. No explanation, no
preamble, no commentary before or after the fence. The file must be complete and
immediately runnable — never truncate, never write "// rest of code here".

Acceptance criteria (15)

  1. the pinned stage section is taller than 200vh
  2. a sticky element with height ~100vh exists
  3. canvas renders non-blank pixels
  4. all four beat texts are present in the DOM
  5. scroll progress drives a transform: a value changes measurably after scrolling 50% of the stage
  6. window.__DEMO__.ready becomes true within 5s
  7. window.__DEMO__.frames advances (>= 20 frames in 1.2s)
  8. window.__DEMO__.errors is empty
  9. zero console errors and zero page errors
  10. zero failed network requests
  11. exactly one <h1> with non-empty text
  12. header, main, footer landmarks present
  13. no horizontal overflow at 390 / 768 / 1440 widths
  14. no horizontal overflow after 2s of animation
  15. prefers-reduced-motion: reduce leaves the page usable and errors empty

More landing page prompts