Skip to content
LandingPagePrompts
Hero Sectiongemini-3.5-flashverified 20/20partial cross-check

Kinetic Type

Split-text mask reveal, velocity-skewed marquee bands and scroll-linked type scale

The exact prompt

DESIGN BRIEF — "Kinetic Type"

Build an editorial, type-first hero for a fictional independent magazine called "OFFCUT".
Typography IS the interface here. No illustrations, no photography — only letters, rules,
and motion.

A. THE SPLIT-TEXT REVEAL (on load, once)
   - Split the <h1> into per-word <span class="w"> wrappers, each containing a
     <span class="wi"> inner. The outer has overflow: hidden; the inner starts at
     translateY(105%) and animates to 0 with a cubic-bezier(.16,1,.3,1) ease over 900ms,
     staggered 65ms per word, driven by the shared rAF loop (compute progress from a
     start timestamp — do not use setTimeout chains).
   - Respect prefers-reduced-motion: skip the reveal entirely and render final state.

B. THE MARQUEE BANDS (three of them)
   - Band 1 (top, 13px, uppercase, 0.3em tracking, opacity .55): slow left drift,
     "OFFCUT — ISSUE 14 — SPRING — " repeated.
   - Band 2 (behind the h1, ~11vw, weight 800, colour transparent with a 1px
     rgba(255,255,255,.13) -webkit-text-stroke, uppercase): fast right drift reading
     "PRINT IS NOT DEAD IT JUST GOT FASTER".
   - Band 3 (below, 15px, weight 600): left drift with the words
     "TYPE / GRID / INK / PAPER / TYPE / GRID / INK / PAPER".
   - All three are driven by ONE rAF loop. Crucially, the drift speed must be modulated
     by scroll velocity: compute `v = (scrollY - lastScrollY)`, smooth it with a 0.12
     lerp, and offset the bands by `baseSpeed + clamp(v * 0.35, -14, 14)` px/frame. The
     bands should visibly surge when the user scrolls fast and settle when they stop.
   - Additionally apply a velocity skew: each band's transform includes
     `skewX(clamp(v * -0.06, -4, 4)deg)` so the type leans into the motion.
   - Seamless wrap: duplicate the band content exactly twice and use a modulo offset of
     half the scroll width. Measure the width once per resize, never per frame.

C. SCROLL-LINKED TYPE SCALE
   - The <h1> scales from 1.0 to 0.82 and its letter-spacing tightens from -0.01em to
     -0.045em as the user scrolls the first 60vh, driven by rAF from a cached scroll
     value. It must feel like the type is being squeezed by the scroll.

D. THE PAGE
   - <header>: a 3-part grid bar — left "OFFCUT" (900 weight, tracking -0.02em),
     centre the issue meta "ISSUE 14 / SPRING", right "Subscribe" as an underlined link
     plus a solid #facc15 pill "Get the print edition" (black text). Hairline rules
     above and below the header.
   - <main>:
       * Hero with the h1: "Slow down and / read the thing." — second line in italic
         serif. Below it a 2-column block (stacked under 720px): a 15px/1.7 paragraph
         and a pull-quote with a 3px #facc15 left border.
       * An "index" section: a numbered list of 5 article titles (01–05) with hairline
         separators; each row is an <a> that on hover slides its title 8px right and
         reveals a small "→". Implement hover motion with CSS transform only.
       * A closing subscribe block: <h2> "One issue. Every quarter. No feed." plus an
         inline email form that validates non-empty and shows an inline success message.
   - <footer>: masthead, three link columns (Read / About / Legal), and a bottom row
     "© 2026 OFFCUT Press — printed in Rotterdam".

E. SYSTEM
   - Palette: paper #f4f1ea, ink #14140f, muted rgba(20,20,15,.6), accent #facc15.
     This page is LIGHT — the only light page in the set. Use dark text on paper.
   - Fonts: "Instrument Serif" (display + italic pull-quotes) and
     "Inter" (UI, 400/600/900) via Google Fonts, with Georgia and system-ui fallbacks.
   - Everything sits on a strict 12-column grid with 24px gutters; hairline rules are
     1px rgba(20,20,15,.16).

---

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 (14)

  1. h1 is split into per-word spans (>= 4 elements with class "w")
  2. at least 3 marquee bands exist
  3. a band transform changes over time
  4. page background is light (luminance > 0.6)
  5. window.__DEMO__.ready becomes true within 5s
  6. window.__DEMO__.frames advances (>= 20 frames in 1.2s)
  7. window.__DEMO__.errors is empty
  8. zero console errors and zero page errors
  9. zero failed network requests
  10. exactly one <h1> with non-empty text
  11. header, main, footer landmarks present
  12. no horizontal overflow at 390 / 768 / 1440 widths
  13. no horizontal overflow after 2s of animation
  14. prefers-reduced-motion: reduce leaves the page usable and errors empty

More hero section prompts